added cairo_anim.nim example

This commit is contained in:
Stefan Salewski 2017-09-08 20:27:12 +02:00
commit b0c8857651
5 changed files with 1567 additions and 26 deletions

View file

@ -642,5 +642,67 @@ code you may catch such exceptions with Nim's `try:` blocks. (You may also modif
an illegal CSS statement -- if the statement is seriously wrong, then you should get an exception from
loadFromData().
== Drawing with Cairo graphics library
The next example shows how we can use the cairo graphics library for drawing on a DrawingArea widget,
and at the same time uses glib timeoutAdd() function to create a timer which periodically calls the
drawing function to create some animations. The code is based on a recent post to the cairo mailing list
and shows a sine wave which is continuously moving to the left.
NOTE: The gobject-introspection generated cairo module was only a minimal stub, because cairo
library does not really support introspection. Now we are using a cairo module which is generated
directly from the cairo C header files with the tool c2nim and then modified to support an high level
API. That modification is still work in progress, so not all cairo functionality is available already.
[[cairo_anim.nim]]
[source,nim]
.cairo_anim.nim
----
# nim c cairo_anim.nim
# https://lists.cairographics.org/archives/cairo/2016-October/027791.html
# Nim version of that plain cairo animation example
import gintro/[gtk, glib, gobject, gio, cairo]
import math
const
NumPoints = 1000
Period = 100.0
proc invalidateCb(w: Widget): bool =
queueDraw(w)
return SOURCE_CONTINUE
proc sineToPoint(x, width, height: int): float =
math.sin(x.float * math.TAU / Period) * height.float * 0.5 + height.float * 0.5
proc drawingAreaDrawCb(widget: DrawingArea; context: Context): bool =
var redrawNumber {.global.} : int
let width = getAllocatedWidth(widget)
let height = getAllocatedHeight(widget)
for i in 1 .. < NumPoints:
context.lineTo(i.float , sineToPoint(i + redrawNumber, width, height))
context.stroke
inc(redrawNumber)
return true # TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
proc activate(app: Application) =
let window = newApplicationWindow(app)
window.title = "Drawing example"
window.defaultSize = (400, 400)
let drawingArea = newDrawingArea()
window.add(drawingArea)
showAll(window)
discard timeoutAdd(1000 div 60, invalidateCb, drawingArea)
connect(drawingArea, "draw", drawingAreaDrawCb)
proc main =
let app = newApplication("org.gtk.example")
connect(app, "activate", activate)
discard run(app)
main()
----
NOTE: Related work: https://github.com/jdmansour/nim-smartgi

44
examples/cairo_anim.nim Normal file
View file

@ -0,0 +1,44 @@
# https://lists.cairographics.org/archives/cairo/2016-October/027791.html
# Nim version of that plain cairo animation example
import gintro/[gtk, glib, gobject, gio, cairo]
import math
const
NumPoints = 1000
Period = 100.0
proc invalidateCb(w: Widget): bool =
queueDraw(w)
return SOURCE_CONTINUE
proc sineToPoint(x, width, height: int): float =
math.sin(x.float * math.TAU / Period) * height.float * 0.5 + height.float * 0.5
proc drawingAreaDrawCb(widget: DrawingArea; context: Context): bool =
var redrawNumber {.global.} : int
let width = getAllocatedWidth(widget)
let height = getAllocatedHeight(widget)
for i in 1 .. < NumPoints:
context.lineTo(i.float , sineToPoint(i + redrawNumber, width, height))
context.stroke
inc(redrawNumber)
return true # TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
proc activate(app: Application) =
let window = newApplicationWindow(app)
window.title = "Drawing example"
window.defaultSize = (400, 400)
let drawingArea = newDrawingArea()
window.add(drawingArea)
showAll(window)
discard timeoutAdd(1000 div 60, invalidateCb, drawingArea)
connect(drawingArea, "draw", drawingAreaDrawCb)
proc main =
let app = newApplication("org.gtk.example")
connect(app, "activate", activate)
discard run(app)
main()

View file

@ -26,14 +26,12 @@ proc findSignal(name, obj: NimNode): string =
n = getType(n)[1]
var ProcID: int
macro mconnect(widget: gobject.Object; signal: string; p: untyped; arg: typed; ignoreArg: bool): typed =
#
macro mconnect(widget: gobject.Object; signal: string; p: untyped; arg: typed; ignoreArg: bool): untyped =
inc(ProcID)
let wt = getType(widget)
#echo "@@@", $(getTypeInst(widget).toStrLit)
let at = getTypeInst(arg)
let wts = ($(wt[1].toStrLit)).replace(":ObjectType")
#echo wts
let ats = $at.toStrLit
let signalName = ($signal).replace("-", "_") # maybe we should just use plain proc names
let procNameCdecl = "connect_for_signal_cdecl_" & signalName & $ProcID
@ -53,14 +51,12 @@ proc $1$2 {.cdecl.} =
r1s.add(")\n")
else:
(sn, wid, num, ahl, all) = sci.split("|")
#echo ahl, all
var resl: string
if ahl.contains("): "):
resl = all.rsplit("): ", 1)[1]
var resu = " $3(cast[$4](h)"
if all.find(";") > 0:
var largs = all.split("; ")
#if largs.len > 0:
largs.delete(0)
largs[^1] = largs[^1].split(")")[0]
var names, types: array[10, string]
@ -76,7 +72,6 @@ proc $1$2 {.cdecl.} =
if h.len > 0:
a1 = h & "(" & a1 & ")"
plainArgs.add(a1)
#var resu = " $3(cast[$4](h)"
for i in 0 .. 9:
if names[i].isNil: break
resu.add(", " & names[i] & "1")
@ -93,27 +88,23 @@ proc $1$2 {.cdecl.} =
r1s.add(resu & "\n")
all = all.replace(")", "; data: pointer)")
r1s = r1s % [$procNameCdecl, all, $p, wts, ats]
#echo r1s
result = parseStmt(r1s)
if not ignoreArg.boolVal:
ahl = ahl.replace(")", "; arg: " & ats & ")")
#echo ahl
if ahl.find(";") > 0:
ahl = "(self: " & wts & ";" & ahl.split(";", 1)[1]
else:
ahl = "(self: " & wts & ")" & ahl.split(")", 1)[1]
#ahl = "(self: " & wts & ")"
let r2s =
if ignoreArg.boolVal:
"""
proc $1(self: $2; p: proc $3) =
proc $1(self: $2; p: proc $3): culong {.discardable.} =
sc$4(self, $5, nil)
$1($6, $7)
""" % [$procName, wts, ahl, signalName, $procNameCdecl, $(widget.toStrLit), $p]
else:
"""
proc $1(self: $2; p: proc $3; a: $4) =
proc $1(self: $2; p: proc $3; a: $4): culong {.discardable.} =
when a is RootRef:
GC_ref(a)
sc$5(self, $6, cast[pointer](a))
@ -125,13 +116,71 @@ proc $1(self: $2; p: proc $3; a: $4) =
sc$5(self, $6, cast[pointer](ar[]))
$1($7, $8, $9)
""" % [$procName, wts, ahl, ats, signalName, $procNameCdecl, $(widget.toStrLit), $p, $(arg.toStrLit)]
#echo r2s
result.add(parseStmt(r2s))
template connect*(widget: gobject.Object; signal: string; p: untyped; arg: typed): typed =
template connect*(widget: gobject.Object; signal: string; p: untyped; arg: typed): untyped =
mconnect(widget, signal, p, arg, false)
template connect*(widget: gobject.Object; signal: string; p: untyped): typed =
template connect*(widget: gobject.Object; signal: string; p: untyped): untyped =
mconnect(widget, signal, p, "", true)
var TimeoutID: int
#
macro timeoutAdd*(interval: Natural; p: untyped; arg: typed): untyped =
inc(TimeoutID)
let ats = $getTypeInst(arg).toStrLit
let procName = "timeoutfunc_" & $TimeoutID
let procNameCdecl = "timeoutfunc_cdecl_" & $TimeoutID
var r1s = """
proc $1(p: pointer): gboolean {.cdecl.} =
let a = cast[$3](p)
result = $2(a).gboolean
#when (a is ref object) or (a is seq):
GC_unref(a)
""" % [$procNameCdecl, $p, ats]
let r2s ="""
proc $1(a: $2): culong {.discardable.} =
when (a is ref object) or (a is seq):
GC_ref(a)
return g_timeout_add_full(PRIORITY_DEFAULT, $3, $4, cast[pointer](a), nil)
else:
var ar: ref $2
new(ar)
deepCopy(ar[], a)
GC_ref(ar)
return g_timeout_add_full(PRIORITY_DEFAULT, $3, $4, cast[pointer](ar), nil)
$1($5)
""" % [$procName, ats, $interval.toStrLit, $procNameCdecl, $arg]
result = parseStmt(r1s & r2s)
var IdleID: int
#
macro idleAdd*(p: untyped; arg: typed): untyped =
inc(IdleID)
let ats = $getTypeInst(arg).toStrLit
let procName = "idlefunc_" & $IdleID
let procNameCdecl = "idlefunc_cdecl_" & $IdleID
var r1s = """
proc $1(p: pointer): gboolean {.cdecl.} =
let a = cast[$3](p)
result = $2(a).gboolean
#when (a is ref object) or (a is seq):
GC_unref(a)
""" % [$procNameCdecl, $p, ats]
let r2s ="""
proc $1(a: $2): culong {.discardable.} =
when (a is ref object) or (a is seq):
GC_ref(a)
return g_idle_add_full(PRIORITY_DEFAULT_IDLE, $3, cast[pointer](a), nil)
else:
var ar: ref $2
new(ar)
deepCopy(ar[], a)
GC_ref(ar)
return g_idle_add_full(PRIORITY_DEFAULT_IDLE, $3, cast[pointer](ar[]), nil)
$1($4)
""" % [$procName, ats, $procNameCdecl, $arg]
result = parseStmt(r1s & r2s)

View file

@ -1,5 +1,5 @@
# High level gobject-introspection based GTK3 bindings for the Nim programming language
# v 0.2 2017-AUG-29
# v 0.2 2017-SEP-08
# (c) S. Salewski 2017
# https://wiki.gnome.org/Projects/GObjectIntrospection
@ -108,7 +108,6 @@ var fixedProcNames = newTable[string, string]()
var mangledNames = newTable[string, string]()
var gdkKeys = newTable[string, seq[string]]()
var allSyms: HashSet[string]
#var scfilter: HashSet[string]
var provInt = newTable[string, seq[string]]()
var interfaceProvider = newTable[string, seq[string]]()
var priList: seq[string]
@ -116,8 +115,8 @@ var knownSyms: HashSet[string]
var suppressType = false
var suppressRaise = false
var droppedSyms = initSet[string]()
var callerAlloc = initSet[string]()
var callerAllocCollector = initSet[string]()
var callerAlloc = initSet[string]()
var callerAllocCollector = initSet[string]()
var privStr = initSet[string]()
var processedFunctions = initSet[string]()
var delayedMethods: seq[(GIBaseInfo, GIFunctionInfo)]
@ -142,6 +141,12 @@ mangledNames.add("2buttonPress", "twoButtonPress")
mangledNames.add("3buttonPress", "threeButtonPress")
mangledNames.add("result", "resu")
proc gBaseInfoGetName(info: GIBaseInfo): string =
result = $(gir.gBaseInfoGetName(info))
let h = $(gir.gBaseInfoGetNamespace(info))
if h == "Gio" and result == "Application":
result = "GApplication"
proc mangleName(s: cstring): string =
result = mysnakeToCamel(s)
if mangledNames.contains(result):
@ -348,7 +353,10 @@ proc genP(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil): (str
else:
arglist = "(cast[ptr " & manglename(gBaseInfoGetName(binfo)) & "00](" & "self.impl)"
if genProxy:
resul = "(self: " & manglename(gBaseInfoGetName(binfo))
var h = manglename(gBaseInfoGetName(binfo))
if (sym.startsWith("gdk_events_get_") or sym.startsWith("gdk_event_get_")) and h == "Event":
h = "SomeEvent"
resul = "(self: " & h
else:
resul = "(self: ptr " & manglename(gBaseInfoGetName(binfo)) & "00"
if m >= 0:
@ -399,6 +407,10 @@ proc genP(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil): (str
(h1, h2, h3) = defaultParameters[sym].split
if name == h1 and str == h2:
str.add(" = " & h3)
if (sym.startsWith("gdk_events_get_") or sym.startsWith("gdk_event_get_")) and str == "Event":
str = "SomeEvent"
resul.add(name & ": " & str)
if j < m:
resul.add("; ")
@ -658,6 +670,11 @@ proc writeUnion(info: GIUnionInfo) =
output.writeLine(" ", name, ": ", genRec(t))
output.writeLine(" ", mangleName(gBaseInfoGetName(info)) & EM & " = ref object")
output.writeLine(" impl*: ptr " & mangleName(gBaseInfoGetName(info)) & "00")
if mangleName(gBaseInfoGetName(info)) == "Event" and moduleNamespace == "gdk":
output.writeLine("type")
output.writeLine(" SomeEvent* = Event | EventButton | EventMotion | EventTouch | EventScroll | EventCrossing | EventTouchpadSwipe | EventTouchpadPinch")
let nMethods = gUnionInfoGetNMethods(info)
for j in 0 .. <nMethods:
let mInfo = gUnionInfoGetMethod(info, j)
@ -887,10 +904,10 @@ proc writeObj(info: GIObjectInfo) =
var xxx = mangleName(gBaseInfoGetName(info))
output.write("proc " & mangleName("sc_" & $gBaseInfoGetName(signalInfo)) & EM & "(self: " & xxx & "; ")
if gCallableInfoGetNArgs(signalInfo) > 0 or gTypeInfoGetTag(zzzu) != GITypeTag.VOID:
output.writeLine(" p: proc (self: ptr " & xxx & "00; " & h & " {.cdecl.}, xdata: pointer = nil) =")
output.writeLine(" p: proc (self: ptr " & xxx & "00; " & h & " {.cdecl.}, xdata: pointer = nil): culong =")
else:
output.writeLine(" p: proc (self: ptr gobject.Object00; " & h & " {.cdecl.}, xdata: pointer = nil) =")
output.write(" discard g_signal_connect_data(self.impl, \"")
output.writeLine(" p: proc (self: ptr gobject.Object00; " & h & " {.cdecl.}, xdata: pointer = nil): culong =")
output.write(" g_signal_connect_data(self.impl, \"")
output.write($gBaseInfoGetName(signalInfo))
output.writeLine("\", cast[GCallback](p), xdata, nil, cast[ConnectFlags](0))")
let nMethods = gObjectInfoGetNMethods(info)
@ -1017,8 +1034,8 @@ proc fixednewCursorFromName*(display: Display; name: string): Cursor =
const GIO_EPI = """
proc run*(self: Application): int =
int(g_application_run(cast[ptr Application00](self.impl), 0, nil))
proc run*(self: GApplication): int =
int(g_application_run(cast[ptr GApplication00](self.impl), 0, nil))
"""

1369
tests/gen.nim.oldv0.2 Normal file

File diff suppressed because it is too large Load diff