fixes and example for libnice

This commit is contained in:
Stefan Salewski 2020-12-02 15:58:41 +01:00
commit 26a6b751b2
5 changed files with 700 additions and 25 deletions

View file

@ -0,0 +1,310 @@
## https://gitlab.freedesktop.org/libnice/libnice/-/blob/master/examples/simple-example.c
## nim c --gc:arc simple_example.nim
## Example using libnice to negotiate a UDP connection between two clients,
## possibly on the same network or behind different NATs and/or stateful
## firewalls.
##
## Run two clients, one controlling and one controlled:
## simple-example 0 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
## simple-example 1 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
##
# https://forum.nim-lang.org/t/3752
when (compiles do: import gintro/gtk):
import gintro/[gtk, glib, gobject, gio, nice]
else:
import gintro/[dummygtk, glib, gobject, gio, nice] # For windows with glib but no gtk
from strutils import `%`, split
from os import paramCount, paramStr
from strutils import parseInt
from posix import INET6_ADDRSTRLEN
when defined(windows):
import winlean
else:
discard # import posix
var
gloop: glib.MainLoop
ioStdin: glib.IOChannel
streamId: int
candidateTypeName = ["host", "srflx", "prflx", "relay"]
stateName = ["disconnected", "gathering", "connecting", "connected", "ready", "failed"]
proc gMsg(s: string) =
stdout.write(s)
flushFile(stdout)
proc gDebug(s: string) =
stdout.write(s)
flushFile(stdout)
proc gError(s: string) =
stderr.write(s)
flushFile(stderr)
proc toStringVal(s: string): Value =
let gtype = gStringGetType()
discard init(result, gtype)
setString(result, s)
proc toIntVal(i: int): Value =
let gtype = typeFromName("gint")
discard init(result, gtype)
setInt(result, i)
proc printLocalData(agent: nice.Agent; streamId: int; componentId: int): int =
var
localUfrag: string
localPassword: string
ipaddr = newString(INET6_ADDRSTRLEN).cstring # not really nice as that cstring is used as out parameter by toString()
cands: seq[Candidate]
result = QuitFailure
block gotoEnd:
if not getLocalCredentials(agent, streamId, localUfrag, localPassword):
break gotoEnd
cands = getLocalCandidates(agent, streamId, componentId)
if cands.len == 0:
break gotoEnd
stdout.write(localUfrag, localPassword)
for el in cands:
toString(el.impl.`addr`, ipaddr)
## (foundation),(prio),(addr),(port),(type)
stdout.write(' ', cast[cstring](addr el.impl.foundation), ',', el.impl.priority, ',', ipaddr, ' ', getPort(el.impl.`addr`),
',', candidateTypeName[el.impl.`type`.ord])
echo ""
# end label
result = QuitSuccess
return result
proc parseCandidate(scand: string; streamId: int): nice.Candidate =
var
cand: nice.Candidate = nil
ntype: nice.CandidateType = CandidateType.host # that initialization is never used!
tokens = scand.split(',', 5)
block gotoEnd:
for i in 0 .. 4:
if tokens[i] == "":
break gotoEnd
#[ should work too
for i, el in candidateTypeName:
var h = false
if tokens[4] == el:
ntype = i
h = true
if not h:
break gotoEnd
]#
var i: int
while i < candidateTypeName.len:
if tokens[4] == candidateTypeName[i]:
ntype = CandidateType(i)
inc(i)
if i == candidateTypeName.len:
break gotoEnd
cand = newCandidate(ntype)
#cand.impl.componentId = 1
cand.setComponentID(1) # we have a few manually created getters and setters already
cand.impl.streamId = uint32(streamId)
cand.impl.transport = nice.CandidateTransport.udp
cand.setFoundation(tokens[0])
cand.impl.priority = uint32(parseInt(tokens[1]))
if not setFromString(cand.impl.`addr`, tokens[2]):
gMsg("failed to parse addr: " & tokens[2])
cand = nil
break gotoEnd
setPort(cand.impl.`addr`, parseInt(tokens[3]))
# end label
return cand
proc parseRemoteData(agent: Agent; streamId: int; componentId: int; line: string): cint =
var
remoteCandidates: seq[Candidate]
lineArgv: seq[string]
ufrag: string
passwd: string
result = QuitFailure
lineArgv = split(line, {' ', '\t', '\n'})
block gotoEnd:
for i, x in lineArgv:
if x.len == 0:
continue
## first two args are remote ufrag and password
if ufrag == "":
ufrag = x
elif passwd == "":
passwd = x
else:
## Remaining args are serialized canidates (at least one is required)
var c: nice.Candidate = parseCandidate(lineArgv[i], streamId)
if c == nil:
gMsg("failed to parse candidate: " & lineArgv[i])
break gotoEnd
remoteCandidates.add(c) # caution prepend in C code!
if ufrag == "" or passwd == "" or remoteCandidates.len == 0:
gMsg("line must have at least ufrag, password, and one candidate")
break gotoEnd
if not setRemoteCredentials(agent, streamId, ufrag, passwd):
gMsg("failed to set remote credentials")
break gotoEnd
## Note: this will trigger the start of negotiation.
if setRemoteCandidates(agent, streamId, componentId, remoteCandidates) < 1:
gMsg("failed to set remote candidates")
break gotoEnd
# end label
result = QuitSuccess
return result
proc stdinRemoteInfoCb(source: glib.IOChannel; cond: glib.IOCondition; agent: nice.Agent): bool =
var
line: string
rval: cint
ret: bool = true
if readLine(source, line) == IOStatus.normal:
## Parse remote candidate list and set it on the agent
rval = parseRemoteData(agent, streamId, 1, line)
if rval == QuitSuccess:
## Return FALSE so we stop listening to stdin since we parsed the
## candidates correctly
ret = false
gDebug("waiting for state READY or FAILED signal...")
else:
write(stderr, "ERROR: failed to parse remote data\n")
echo("Enter remote data (single line, no wrapping):")
stdout.write("> ")
flushFile(stdout)
return ret
proc cbCandidateGatheringDone(agent: nice.Agent; streamId: int) =
gDebug("SIGNAL candidate gathering done\n")
## Candidate gathering is done. Send our local candidates on stdout
echo("Copy this line to remote client:")
echo(" ")
discard printLocalData(agent, streamId, 1)
echo("")
## Listen on stdin for the remote candidate list
echo("Enter remote data (single line, no wrapping):")
addWatch(ioStdin, glib.PRIORITY_DEFAULT, IOCondition.in , stdinRemoteInfoCb, agent)
stdout.write("> ")
flushFile(stdout)
proc cbNewSelectedPair(agent: nice.Agent; streamId: int; componentId: int; lfoundation: string; rfoundation: string) =
gDebug("SIGNAL: selected pair " & lfoundation & " " & rfoundation)
proc stdinSendDataCb(source: IOChannel; cond: IOCondition; agent: nice.Agent): bool =
var line: string
if readLine(source, line) == IOStatus.normal:
discard send(agent, streamId, 1, line.len, line)
stdout.write("> ")
flushFile(stdout)
else:
discard send(agent, streamId, 1, 1, "\x00")
## Ctrl-D was pressed.
quit(gloop)
return true
proc cbComponentStateChanged(agent: nice.Agent; streamId: int; componentId: int; state: int) =
gDebug("SIGNAL: state changed $1 $2 $3[$4]\n" % [$streamId, $componentId, stateName[state], $state])
if state == ComponentState.connected.ord:
var
local: Candidate
remote: nice.Candidate
## Get current selected candidate pair and print IP address used
if getSelectedPair(agent, streamId, componentId, local, remote):
var ipaddr = newString(INET6_ADDRSTRLEN).cstring
toString(local.impl.`addr`, ipaddr)
echo("\nNegotiation complete: ([$1]:$2,", [$ipaddr, $getPort(local.impl.`addr`)])
toString(remote.impl.`addr`, ipaddr)
echo(" [$1]:$2)" % [$ipaddr, $getPort(remote.impl.`addr`)])
## Listen to stdin and send data written to it
echo("\nSend lines to remote (Ctrl-D to quit):")
addWatch(ioStdin, glib.PRIORITY_DEFAULT, IOCondition.in , stdinSendDataCb, agent)
stdout.write("> ")
flushFile(stdout)
elif state == ComponentState.failed.ord:
quit(gloop)
proc cbNiceRecv(agent: nice.Agent; streamID: cuint; componentID: cuint; len: cuint; buf: cstring) = #{.cdecl.} =
if len == 1 and buf[0] == '\x00':
glib.quit(gloop)
discard stdout.writeBuffer(buf, len)
flushfile(stdout)
proc main =
var
agent: nice.Agent
stunAddr: string
stunPort: int
controlling: int
## Parse arguments
let argc = paramCount() + 1
var argv = newSeq[string](argc)
for i in 0 ..< argc:
argv[i] = paramStr(i)
if argc > 4 or argc < 2 or argv[1].len > 1:
write(stderr, "Usage: $1 0|1 stun_addr [stun_port]\n" % [argv[0]])
quit(QuitFailure)
controlling = argv[1][0].ord - '0'.ord
if controlling != 0 and controlling != 1:
write(stderr, "Usage: $1 0|1 stun_addr [stun_port]\n" % [argv[0]])
quit(QuitFailure)
if argc > 2:
stunAddr = argv[2]
if argc > 3:
stunPort = parseInt(argv[3])
else:
stunPort = 3478
gDebug("Using stun server \'[$1]:$2\'\n" % [$stunAddr, $stunPort])
gio.networkingInit()
gloop = newMainLoop(nil, false)
# https://gist.github.com/demotomohiro/c9d9ad7e17639c6acb9bd2be7c204f3f
when hostOS == "windows":
ioStdin = win32NewFd(system.stdin.getFileHandle)
else:
ioStdin = unixNew(system.stdin.getFileHandle)
## Create the nice agent
agent = nice.newAgent(glib.getContext(gloop), nice.CompatibilityRfc5245)
if agent == nil:
gError("Failed to create agent")
## Set the STUN settings and controlling mode
if stunAddr.len > 0:
setProperty(agent, "stun-server", toStringVal(stunAddr))
setProperty(agent, "stun-server-port", toIntVal(stunPort))
setProperty(agent, "controlling-mode", toIntVal(controlling))
## Connect to the signals
agent.connect("candidate-gathering-done", cbCandidateGatheringDone)
agent.connect("new-selected-pair", cbNewSelectedPair)
agent.connect("component-state-changed", cbComponentStateChanged)
## Create a new stream with one component
streamId = addStream(agent, 1)
if streamId == 0:
gError("Failed to add stream")
## Attach to the component to receive the data
## Without this call, candidates cannot be gathered
attachRecv(agent, streamId.cuint, 1, getContext(gloop), cbNiceRecv)
## Start gathering local candidates
if not gatherCandidates(agent, streamId):
gError("Failed to start candidate gathering")
gDebug("waiting for candidate-gathering-done signal...")
## Run the mainloop. Everything else will happen asynchronously
## when the candidates are done gathering.
run(gloop)
quit(QuitSuccess)
main()

67
gintro/gimplglib.nim Normal file
View file

@ -0,0 +1,67 @@
import macros
from strutils import `%`
macro mAddWatch(ch: IOChannel; pri: cint; cond: IOCondition; p: untyped; arg: typed; ignoreArg: bool): untyped =
var IdleID {.compileTime, global.}: int
inc(IdleID)
let ats = $getTypeInst(arg).toStrLit
let procName = "giofunc_" & $IdleID
let procNameCdecl = "giofunc_cdecl_" & $IdleID
var r1s = """
proc $1(self: ptr IOChannel00; cond: IOCondition; userData: pointer): gboolean {.cdecl.} =
var ch1 {.global.}: glib.IOChannel
if ch1.isNil:
new ch1
GC_ref(ch1) # never call destroy/finalizer on this global variable
ch1.ignoreFinalizer = true
ch1.impl = self
discard $2(ch1, cond"""
if not ignoreArg.boolVal:
if getTypeInst(arg).typeKind == ntyRef:
r1s.add(", cast[$3](userdata)")
else:
r1s.add(", cast[$3](userdata)")
r1s.add(")\n")
r1s = r1s % [$procNameCdecl, $p, ats]
let r2s =
if ignoreArg.boolVal:
"""
proc $1(self: IOChannel; pri: cint; cond: IOCondition) =
g_io_add_watch_full(cast[ptr IOChannel00](self.impl), pri, cond, $5, nil, nil)
$1($5, $3, $4)
""" % [$procName, ats, $pri, $cond, $procNameCdecl, $(arg.toStrLit), $(ch.toStrLit)]
else:
"""
proc $1(self: IOChannel; pri: cint; cond: IOCondition; a: $2) =
when (a is ref object):
GC_ref(a)
discard g_io_add_watch_full(cast[ptr IOChannel00](self.impl), pri, cond, $5, cast[pointer](a), nil)
else:
var ar: ref $2
new(ar)
#deepCopy(ar[], a)
ar[] = a
GC_ref(ar)
discard g_io_add_watch_full(cast[ptr IOChannel00](self.impl), pri, cond, $5, cast[pointer](ar[]), nil)
$1($7, $3, $8.$4, $6)
""" % [$procName, ats, $(pri.toStrLit), $(cond.toStrLit), $procNameCdecl, $(arg.toStrLit), $(ch.toStrLit), $(getTypeInst(
cond).toStrLit)]
#echo r1s
#echo r2s
result = parseStmt(r1s & r2s)
template addWatch*(ch: IOChannel; pri: int; cond: IOCondition; p: untyped; arg: typed): untyped =
maddWatch(ch, pri.cint, cond, p, arg, false)
template addWatch*(ch: IOChannel; pri: int; cond: IOCondition; p: untyped): untyped =
maddWatch(ch, pri.cint, cond, p, "", true)

View file

@ -172,7 +172,7 @@ $1($5)
proc $1(self: DrawingArea; a: $2) = proc $1(self: DrawingArea; a: $2) =
when (a is ref object): when (a is ref object):
GC_ref(a) GC_ref(a)
gtk_drawing_area_set_draw_func(self.impl, $3, cast[pointer](a), nil) gtk_drawing_area_set_draw_func(cast[ptr DrawingArea00](self.impl), $3, cast[pointer](a), nil)
else: else:
var ar: ref $2 var ar: ref $2
new(ar) new(ar)
@ -183,8 +183,8 @@ proc $1(self: DrawingArea; a: $2) =
$1($5, $4) $1($5, $4)
""" % [$procName, ats, $procNameCdecl, $(arg.toStrLit), $(da.toStrLit)] """ % [$procName, ats, $procNameCdecl, $(arg.toStrLit), $(da.toStrLit)]
echo r1s #echo r1s
echo r2s #echo r2s
result = parseStmt(r1s & r2s) result = parseStmt(r1s & r2s)
template setDrawFunc*(da: DrawingArea; p: untyped; arg: typed): untyped = template setDrawFunc*(da: DrawingArea; p: untyped; arg: typed): untyped =

105
gintro/gimplnice.nim Normal file
View file

@ -0,0 +1,105 @@
proc nice_agent_attach_recv*(self: ptr Object00; streamID: cuint; componentID: cuint; ctx: ptr glib.MainContext00;
fn: AgentRecvFunc; data: pointer): gboolean {.importc, libprag.}
# we will never use this proc, as we use the macro!
proc attachRecv*(self: Agent; streamID: int; componentID: int; ctx: MainContext; fn: AgentRecvFunc; data: pointer): bool =
toBool(nice_agent_attach_recv(self.impl, streamID.cuint, componentID.cuint, ctx.impl, fn, data))
import macros
macro mattachRecv(ag: Agent; streamID: cuint; componentID: cuint; ctx: glib.MainContext; p: untyped; arg: typed;
ignoreArg: bool): untyped =
var IdleID {.compileTime, global.}: int
inc(IdleID)
let ats = $getTypeInst(arg).toStrLit
let procName = "recvfunc_" & $IdleID
let procNameCdecl = "recvfunc_cdecl_" & $IdleID
var r1s = """
proc $1(self: ptr Agent00; streamId: uint32; componentId: uint32; len: uint32; buf: cstring; userData: pointer) {.cdecl.} =
var ag1 {.global.}: nice.Agent
if ag1.isNil:
new ag1
GC_ref(ag1) # never call destroy/finalizer on this global variable
ag1.ignoreFinalizer = true
ag1.impl = self
$2(ag1, streamID, componentID, len, buf"""
if not ignoreArg.boolVal:
if getTypeInst(arg).typeKind == ntyRef:
r1s.add(", cast[$3](userdata)")
else:
r1s.add(", cast[$3](userdata)")
r1s.add(")\n")
r1s = r1s % [$procNameCdecl, $p, ats]
let r2s =
if ignoreArg.boolVal:
"""
proc $1(self: Agent; streamId: uint32; componentId: uint32; ctx: glib.MainContext) =
#gtk_drawing_area_set_draw_func(self.impl, $3, nil, nil)
#g_io_add_watch_full(cast[ptr IOChannel00](self.impl), pri, cond, $5, nil, nil)
discard nice_agent_attach_recv(cast[ptr Agent00](self.impl), streamId, componentId, cast[ptr MainContext00](ctx.impl), $7, nil)#, userData)
#gtk_drawing_area_set_draw_func(cast[ptr DrawingArea00](self.impl), $3, nil, nil)
$1($5, $3, $4, $6)
""" % [$procName, ats, $(streamID.toStrLit), $(componentID.toStrLit), $(ag.toStrLit), $(ctx.toStrLit), $procNameCdecl, $(arg.toStrLit)]
else:
"""
proc $1(self: Agent; streamId: uint32; componentId: uint32; a: $2) =
when (a is ref object):
GC_ref(a)
discard nice_agent_attach_recv(cast[ptr Agent00](self.impl), streamId, componentId, $7, cast[pointer](a))
#discard g_io_add_watch_full(cast[ptr IOChannel00](self.impl), pri, cond, $5, cast[pointer](a), nil)
else:
var ar: ref $2
new(ar)
#deepCopy(ar[], a)
ar[] = a
GC_ref(ar)
discard nice_agent_attach_recv(cast[ptr Agent00](self.impl), streamId, componentId, $7, cast[pointer](a[]))
#discard g_io_add_watch_full(cast[ptr IOChannel00](self.impl), pri, cond, $5, cast[pointer](ar[]), nil)
$1($5, $3, $4, $5, $8)
""" % [$procName, ats, $streamID, $componentID, $(ag.toStrLit), "xx", $procNameCdecl, $(arg.toStrLit)]
#echo r1s
#echo r2s
result = parseStmt(r1s & r2s)
template attachRecv*(ag: Agent; streamID: cuint; componentID: cuint; ctx: glib.MainContext; p: untyped; arg: typed): untyped =
mattachRecv(ag, streamID, componentID, ctx, p, arg, false)
template attachRecv*(ag: Agent; streamID: cuint; componentID: cuint; ctx: glib.MainContext; p: untyped): untyped =
mattachRecv(ag, streamID, componentID, ctx, p, "", true)
proc setAddress*(c: Candidate; s: cstring): bool =
toBool(nice_address_set_from_string(c.impl.addr, s))
proc setPort*(c: Candidate; p: uint32) =
nice_address_set_port(c.impl.addr, p)
proc setTransport*(c: Candidate; t: CandidateTransport) =
c.impl.transport = t
proc setcomponentID*(c: Candidate; id: uint32) =
c.impl.componentId = id
proc setStreamID*(c: Candidate; id: uint32) =
c.impl.streamId = id
proc setPriority*(c: Candidate; pri: uint32) =
c.impl.priority = pri
proc setFoundation*(c: Candidate; s: cstring) =
var i: int
while i < c.impl.foundation.high:
c.impl.foundation[i] = s[i].int8
if s[i] == '\x0':
break
inc(i)
c.impl.foundation[^1] = 0

View file

@ -1,6 +1,6 @@
# High level gobject-introspection based GTK3/GTK4 bindings for the Nim programming language # High level gobject-introspection based GTK3/GTK4 bindings for the Nim programming language
# nimpretty --maxLineLen:130 gen.nim # nimpretty --maxLineLen:130 gen.nim
# v 0.8.4 2020-NOV-18 # v 0.8.4 2020-DEC-02
# (c) S. Salewski 2018 # (c) S. Salewski 2018
# usefull for finding death code: # usefull for finding death code:
@ -440,7 +440,7 @@ gobject.EnumClass
gtk.RecentData gtk.RecentData
glib.SList glib.SList
glib.List glib.List
nice.Candidate nice.Address
""" """
const autoCallerAlloc = """ const autoCallerAlloc = """
@ -612,6 +612,7 @@ type
cstrA cstrA
intA0 intA0
glist glist
gslist
namedAX namedAX
intAX intAX
@ -674,6 +675,23 @@ proc newGenRec(t: GITypeInfo; genProxy = false): RecRes =
result.namePlainNS = result[0] result.namePlainNS = result[0]
result.name00 = result[0] result.name00 = result[0]
return return
if tag == GITypeTag.GSLIST:
let arrayType = gTypeInfoGetParamType(t, 0)
let atag = gTypeInfoGetTag(arrayType) # INTERFACE or UTF8
if atag == GITypeTag.INTERFACE:
let aface = gTypeInfoGetInterface(arrayType)
result[3] = gBaseInfoGetType(aface)
var child = newGenRec(arrayType, genProxy = true).namePlain # [0]
child = mangleType(mangleName(child))
result[0] = "ptr glib.SList"
result[1] = gslist
result[2] = child
result.namePlain = result[0]
result.namePlainNS = result[0]
result.name00 = result[0]
return
if tag == GITypeTag.ARRAY: if tag == GITypeTag.ARRAY:
result.flags.incl(RecResFlag.array) result.flags.incl(RecResFlag.array)
let toa = gTypeInfoGetArrayType(t) # type of array let toa = gTypeInfoGetArrayType(t) # type of array
@ -921,7 +939,6 @@ proc genPars(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil; ge
str = ngr.namePlain str = ngr.namePlain
else: else:
str = ngr.name00 str = ngr.name00
#var istr = str
if genProxy and ngr.res == glist: if genProxy and ngr.res == glist:
result.tempRes = " var tempResGL = seq2GList($1)\n" % [mangleName(gBaseInfoGetName(arg))] result.tempRes = " var tempResGL = seq2GList($1)\n" % [mangleName(gBaseInfoGetName(arg))]
if gArgInfoGetOwnershipTransfer(arg) == GITransfer.NOTHING: if gArgInfoGetOwnershipTransfer(arg) == GITransfer.NOTHING:
@ -930,6 +947,16 @@ proc genPars(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil; ge
str = "var seq[$1]" % [ngr.childname] str = "var seq[$1]" % [ngr.childname]
else: else:
str = "seq[$1]" % [ngr.childname] str = "seq[$1]" % [ngr.childname]
if genProxy and ngr.res == gslist:
result.tempRes = " var tempResGL = seq2GSList($1)\n" % [mangleName(gBaseInfoGetName(arg))]
if gArgInfoGetOwnershipTransfer(arg) == GITransfer.NOTHING:
result.tempResFree = " g_slist_free(tempResGL)\n"
if str.startsWith("var "):
str = "var seq[$1]" % [ngr.childname]
else:
str = "seq[$1]" % [ngr.childname]
if (ngr.flags * {RecResFlag.unamedA, namedA} != {}) and genProxy and not gTypeInfoIsZeroTerminated(t): if (ngr.flags * {RecResFlag.unamedA, namedA} != {}) and genProxy and not gTypeInfoIsZeroTerminated(t):
assert ngr.flags.contains(RecResFlag.array) assert ngr.flags.contains(RecResFlag.array)
assert not (ngr.flags.contains(RecResFlag.zeroTerminated)) assert not (ngr.flags.contains(RecResFlag.zeroTerminated))
@ -980,12 +1007,16 @@ proc genPars(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil; ge
else: else:
if ngr.res == glist: if ngr.res == glist:
result.arglist.add("tempResGL") result.arglist.add("tempResGL")
elif ngr.res == gslist:
result.arglist.add("tempResGL")
elif ngr.flags.contains(RecResFlag.unamedA) and (gArgInfoGetDirection(arg) == GIDirection.IN or gArgInfoIsCallerAllocates(arg)): elif ngr.flags.contains(RecResFlag.unamedA) and (gArgInfoGetDirection(arg) == GIDirection.IN or gArgInfoIsCallerAllocates(arg)):
assert ngr.flags.contains(RecResFlag.array) assert ngr.flags.contains(RecResFlag.array)
result.arglist.add("cast[" & ngr.name00 & "](unsafeaddr($1[0]))" % name) result.arglist.add("cast[" & ngr.name00 & "](unsafeaddr($1[0]))" % name)
elif (ngr.flags * {RecResFlag.namedA, cstringA} != {}) and gArgInfoGetDirection(arg) == GIDirection.IN: elif (ngr.flags * {RecResFlag.namedA, cstringA} != {}) and gArgInfoGetDirection(arg) == GIDirection.IN:
assert ngr.flags.contains(RecResFlag.array) assert ngr.flags.contains(RecResFlag.array)
result.arglist.add("seq2$1" % [str.replace("00").capitalizeAscii.fixedName] & '(' & name & ", " & StringArrayName & "$1)" % [num]) result.arglist.add("seq2$1" % [str.replace("00").capitalizeAscii.fixedName] & '(' & name & ", " & StringArrayName &
"$1)" % [num])
num = "2" num = "2"
elif needTypeConv(str, ngr) and gArgInfoGetDirection(arg) in {GIDirection.OUT, GIDirection.INOUT}: elif needTypeConv(str, ngr) and gArgInfoGetDirection(arg) in {GIDirection.OUT, GIDirection.INOUT}:
result.arglist.add(name.strip(chars = {'`'}) & "_00") result.arglist.add(name.strip(chars = {'`'}) & "_00")
@ -1103,6 +1134,9 @@ proc genPars(info: GICallableInfo; genProxy = false; binfo: GIBaseInfo = nil; ge
r = ngrRet.name00 r = ngrRet.name00
if genProxy and ngrRet.res == glist: if genProxy and ngrRet.res == glist:
r = "seq[$1]" % [ngrRet.childname] r = "seq[$1]" % [ngrRet.childname]
if genProxy and ngrRet.res == gslist:
r = "seq[$1]" % [ngrRet.childname]
var hhh = ngrRet.namePlainNS var hhh = ngrRet.namePlainNS
if hhh.startsWith("gtk4", "gdk4"): if hhh.startsWith("gtk4", "gdk4"):
hhh.delete(3, 3) hhh.delete(3, 3)
@ -1154,7 +1188,6 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo) =
assert(gCallableInfoGetCallerOwns(minfo) in {GITransfer.NOTHING, EVERYTHING}) # both occur assert(gCallableInfoGetCallerOwns(minfo) in {GITransfer.NOTHING, EVERYTHING}) # both occur
methodBuffer.write(pars.tempRes) methodBuffer.write(pars.tempRes)
methodBuffer.writeLine(" let gobj = " & sym & pars.arglist) methodBuffer.writeLine(" let gobj = " & sym & pars.arglist)
methodBuffer.write(pars.tempResFree)
checkForGerror() checkForGerror()
if gCallableInfoMayReturnNull(minfo): if gCallableInfoMayReturnNull(minfo):
methodBuffer.writeLine(" if gobj.isNil:") methodBuffer.writeLine(" if gobj.isNil:")
@ -1373,7 +1406,7 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo) =
if sym in ["g_quark_from_static_string", "g_error_free", "g_object_get_qdata", "g_object_ref_sink", "g_object_unref", if sym in ["g_quark_from_static_string", "g_error_free", "g_object_get_qdata", "g_object_ref_sink", "g_object_unref",
"g_timeout_add_full", "g_object_is_floating", "g_timeout_add_full", "g_object_is_floating",
"g_type_from_name", "g_type_check_instance_is_a", "g_idle_add_full", "g_quark_try_string", "vte_regex_unref", "g_type_from_name", "g_type_check_instance_is_a", "g_idle_add_full", "g_quark_try_string", "vte_regex_unref",
"gtk_builder_get_object", "g_action_map_add_action", "gtk_drawing_area_set_draw_func"]: "gtk_builder_get_object", "g_action_map_add_action", "gtk_drawing_area_set_draw_func", "g_io_add_watch_full"]:
methodBuffer.write("\nproc " & sym & EM & pars.plist) methodBuffer.write("\nproc " & sym & EM & pars.plist)
methodBuffer.writeLine(" {.\n importc, ", libprag, ".}") methodBuffer.writeLine(" {.\n importc, ", libprag, ".}")
if sym.startsWith("vte_regex_unref"): # this function is special, as it returns the object if sym.startsWith("vte_regex_unref"): # this function is special, as it returns the object
@ -1405,6 +1438,13 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo) =
if (gFunctionInfoGetFlags(mInfo).int and GIFunctionInfoFlags.IS_CONSTRUCTOR.int) != 0 or isProxyCandidate(ret2): if (gFunctionInfoGetFlags(mInfo).int and GIFunctionInfoFlags.IS_CONSTRUCTOR.int) != 0 or isProxyCandidate(ret2):
if info != nil: if info != nil:
asym = asym.replace("new", "new" & manglename(gBaseInfoGetName(info))) asym = asym.replace("new", "new" & manglename(gBaseInfoGetName(info)))
else: # i.e. "pango_attr_background_new"
if asym.endsWith("New"):
asym.setLen(asym.len - 3)
asym[0] = asym[0].toUpperAscii
asym = "new" & asym
#discard #asym =
#if sym == "pango_attr_background_new": echo "sss", info != nil, asym
asym = fixedProcNames.getOrDefault(sym, asym) asym = fixedProcNames.getOrDefault(sym, asym)
# BLOCKMARK8 : 3 cases: constructor, isProxyCandidate(ret2), plain else # BLOCKMARK8 : 3 cases: constructor, isProxyCandidate(ret2), plain else
if (gFunctionInfoGetFlags(mInfo).int and GIFunctionInfoFlags.IS_CONSTRUCTOR.int) != 0: if (gFunctionInfoGetFlags(mInfo).int and GIFunctionInfoFlags.IS_CONSTRUCTOR.int) != 0:
@ -1430,7 +1470,8 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo) =
if itt == 2: if itt == 2:
depStr = " {.deprecated.}" depStr = " {.deprecated.}"
methodBuffer.writeLine(hhh & depStr & " =") methodBuffer.writeLine(hhh & depStr & " =")
methodBuffer.write(pars.tempRes) # caution, gobjectTemp() may write tempRes too. if not isGObject:
methodBuffer.write(pars.tempRes) # caution, gobjectTemp() may write tempRes too.
methodBuffer.write(pars.arrLex) methodBuffer.write(pars.arrLex)
methodBuffer.write(sss) methodBuffer.write(sss)
if gCallableInfoCanThrowGerror(minfo): if gCallableInfoCanThrowGerror(minfo):
@ -1620,7 +1661,8 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo) =
#assert not (v[1].flags.contains(RecResFlag.zeroTerminated)) #assert not (v[1].flags.contains(RecResFlag.zeroTerminated))
methodBuffer.writeLine(" var $1x$2: array[$3, pointer]" % [StringArrayName, multi, $StringArrayEntries]) methodBuffer.writeLine(" var $1x$2: array[$3, pointer]" % [StringArrayName, multi, $StringArrayEntries])
methodBuffer.writeLine(" var $1$2: cstringArray = cast[cstringArray](addr $1x$2)" % [StringArrayName, multi]) methodBuffer.writeLine(" var $1$2: cstringArray = cast[cstringArray](addr $1x$2)" % [StringArrayName, multi])
methodBuffer.writeLine(" var $1_00 = seq2$2($3, $4$5)" % [k, v[0].replace("00").capitalizeAscii, k, StringArrayName, multi]) methodBuffer.writeLine(" var $1_00 = seq2$2($3, $4$5)" % [k, v[0].replace("00").capitalizeAscii, k,
StringArrayName, multi])
if multi == "": if multi == "":
multi = "1" multi = "1"
elif v[1].flags.contains(RecResFlag.unamedA): elif v[1].flags.contains(RecResFlag.unamedA):
@ -1644,17 +1686,47 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo) =
methodBuffer.writeLine(" result = glistObjects2seq($1, resul0, $2)" % [ngrRet.childName, $elTransferFull]) methodBuffer.writeLine(" result = glistObjects2seq($1, resul0, $2)" % [ngrRet.childName, $elTransferFull])
if ngrRet.infoType == GIInfoType.STRUCT: if ngrRet.infoType == GIInfoType.STRUCT:
if gBaseInfoGetName(gTypeInfoGetInterface(gTypeInfoGetParamType(ret2, 0))) notin ["Atom", "Variant"]: # care later if gBaseInfoGetName(gTypeInfoGetInterface(gTypeInfoGetParamType(ret2, 0))) notin ["Atom", "Variant"]: # care later
methodBuffer.writeLine(" result = glistStructs2seq[$1](resul0)" % [ngrRet.childName]) methodBuffer.writeLine(" result = glistStructs2seq[$1](resul0, $2)" % [ngrRet.childName, $(
gCallableInfoGetCallerOwns(minfo) != GITransfer.EVERYTHING)])
if gCallableInfoGetCallerOwns(minfo) != GITransfer.NOTHING: if gCallableInfoGetCallerOwns(minfo) != GITransfer.NOTHING:
methodBuffer.writeLine(" g_list_free(resul0)") methodBuffer.writeLine(" g_list_free(resul0)") # only free the list itself, the elements are moved to the seq
else: else:
if ngrRet.infoType == GIInfoType.OBJECT or ngrRet.infoType == GIInfoType.INTERFACE: if ngrRet.infoType == GIInfoType.OBJECT or ngrRet.infoType == GIInfoType.INTERFACE:
if ngrRet.infoType != GIInfoType.INTERFACE: if ngrRet.infoType != GIInfoType.INTERFACE:
assert findFreeObject(gTypeInfoGetInterface(gTypeInfoGetParamType(ret2, 0))).len == 0 assert findFreeObject(gTypeInfoGetInterface(gTypeInfoGetParamType(ret2, 0))).len == 0
methodBuffer.writeLine(" result = glistObjects2seq($1, $2, $3)" % [ngrRet.childName, sym & pars.arglist, $elTransferFull]) methodBuffer.writeLine(" result = glistObjects2seq($1, $2, $3)" % [ngrRet.childName, sym & pars.arglist,
$elTransferFull])
else: else:
methodBuffer.writeLine(" discard") methodBuffer.writeLine(" discard")
assert(gCallableInfoGetCallerOwns(minfo) in {GITransfer.EVERYTHING, GITransfer.NOTHING, GITransfer.CONTAINER}) assert(gCallableInfoGetCallerOwns(minfo) in {GITransfer.EVERYTHING, GITransfer.NOTHING, GITransfer.CONTAINER})
elif ngrRet.res == gslist:
var elTransferFull = gCallableInfoGetCallerOwns(minfo) == GITransfer.EVERYTHING
if gCallableInfoCanThrowGerror(minfo) or gCallableInfoMayReturnNull(minfo) or gCallableInfoGetCallerOwns(minfo) !=
GITransfer.NOTHING:
methodBuffer.writeLine(" let resul0 = " & sym & pars.arglist)
checkForGerror()
if gCallableInfoMayReturnNull(minfo):
methodBuffer.writeLine(" if resul0.isNil:")
methodBuffer.writeLine(" return")
if ngrRet.infoType == GIInfoType.OBJECT or ngrRet.infoType == GIInfoType.INTERFACE:
methodBuffer.writeLine(" result = gslistObjects2seq($1, resul0, $2)" % [ngrRet.childName, $elTransferFull])
if ngrRet.infoType == GIInfoType.STRUCT:
if gBaseInfoGetName(gTypeInfoGetInterface(gTypeInfoGetParamType(ret2, 0))) notin ["Atom", "Variant"]: # care later
methodBuffer.writeLine(" result = gslistStructs2seq[$1](resul0, $2)" % [ngrRet.childName, $(
gCallableInfoGetCallerOwns(minfo) != GITransfer.EVERYTHING)])
if gCallableInfoGetCallerOwns(minfo) != GITransfer.NOTHING:
methodBuffer.writeLine(" g_slist_free(resul0)") # only free the list itself, the elements are moved to the seq
else:
if ngrRet.infoType == GIInfoType.OBJECT or ngrRet.infoType == GIInfoType.INTERFACE:
if ngrRet.infoType != GIInfoType.INTERFACE:
assert findFreeObject(gTypeInfoGetInterface(gTypeInfoGetParamType(ret2, 0))).len == 0
methodBuffer.writeLine(" result = gslistObjects2seq($1, $2, $3)" % [ngrRet.childName, sym & pars.arglist,
$elTransferFull])
else:
methodBuffer.writeLine(" discard")
assert(gCallableInfoGetCallerOwns(minfo) in {GITransfer.EVERYTHING, GITransfer.NOTHING, GITransfer.CONTAINER})
elif ngrRet.flags.contains(RecResFlag.unamedA) and ngrRet.res == intAX: elif ngrRet.flags.contains(RecResFlag.unamedA) and ngrRet.res == intAX:
# https://discourse.gnome.org/t/g-key-file-get-integer-list-transfer-container/4729 # https://discourse.gnome.org/t/g-key-file-get-integer-list-transfer-container/4729
# for fundamental types GITransfer.EVERYTHING and GITransfer.CONTAINER are equivalent # for fundamental types GITransfer.EVERYTHING and GITransfer.CONTAINER are equivalent
@ -1924,6 +1996,7 @@ proc writeUnion(info: GIUnionInfo) =
output.writeLine(" ", mangleName(gBaseInfoGetName(info)) & EM & " {.pure, byRef, union.} = object") output.writeLine(" ", mangleName(gBaseInfoGetName(info)) & EM & " {.pure, byRef, union.} = object")
else: else:
output.writeLine(" ", mangleName(gBaseInfoGetName(info)) & "00" & EM & " {.pure, union.} = object") output.writeLine(" ", mangleName(gBaseInfoGetName(info)) & "00" & EM & " {.pure, union.} = object")
if WriteFields or callerAlloc.contains(($gBaseInfoGetNamespace(info)).toLowerAscii & '.' & mangleName(gBaseInfoGetName(info))): if WriteFields or callerAlloc.contains(($gBaseInfoGetNamespace(info)).toLowerAscii & '.' & mangleName(gBaseInfoGetName(info))):
for j in 0.cint ..< info.gUnionInfoGetNFields(): for j in 0.cint ..< info.gUnionInfoGetNFields():
let field = info.gUnionInfoGetField(j) let field = info.gUnionInfoGetField(j)
@ -2006,6 +2079,7 @@ proc keymapKeyArrayToSeq(s: ptr KeymapKey; n: int): seq[KeymapKey] =
""" % [$StringArrayEntries] """ % [$StringArrayEntries]
proc writeStruct(info: GIStructInfo) = proc writeStruct(info: GIStructInfo) =
if gStructInfoIsGtypeStruct(info) and gBaseInfoGetName(info) != "ObjectClass": if gStructInfoIsGtypeStruct(info) and gBaseInfoGetName(info) != "ObjectClass":
return # we should not need the class and interface structs return # we should not need the class and interface structs
if gBaseInfoGetName(info).endsWith("Private"): # since v0.5.3 we do not write private structs if gBaseInfoGetName(info).endsWith("Private"): # since v0.5.3 we do not write private structs
@ -2017,15 +2091,26 @@ proc writeStruct(info: GIStructInfo) =
output.writeLine(" " & mangleName(gBaseInfoGetName(info)) & "00" & EM & " = Event00") output.writeLine(" " & mangleName(gBaseInfoGetName(info)) & "00" & EM & " = Event00")
output.writeLine(" " & mangleName(gBaseInfoGetName(info)) & EM & " = Event") output.writeLine(" " & mangleName(gBaseInfoGetName(info)) & EM & " = Event")
return return
if callerAlloc.contains(($gBaseInfoGetNamespace(info)).toLowerAscii & '.' & mangleName(gBaseInfoGetName(info))):
if gBaseInfoGetName(info) == "Address" and moduleNamespace == "nice":
#import nativesockets
# See https://gitlab.freedesktop.org/libnice/libnice/-/blob/master/agent/address.h
output.writeLine(" Address* {.pure, byRef, union.} = object")
output.writeLine(" `addr`*: SockAddr")
output.writeLine(" ip4*: Sockaddr_in")
output.writeLine(" ip6*: Sockaddr_in6")
elif callerAlloc.contains(($gBaseInfoGetNamespace(info)).toLowerAscii & '.' & mangleName(gBaseInfoGetName(info))):
output.writeLine(" ", mangleName(gBaseInfoGetName(info)) & EM & " {.pure, byRef.} = object") output.writeLine(" ", mangleName(gBaseInfoGetName(info)) & EM & " {.pure, byRef.} = object")
else: else:
output.writeLine(" ", mangleName(gBaseInfoGetName(info)) & "00" & EM & " {.pure.} = object") output.writeLine(" ", mangleName(gBaseInfoGetName(info)) & "00" & EM & " {.pure.} = object")
if WriteFields or callerAlloc.contains(($gBaseInfoGetNamespace(info)).toLowerAscii & '.' & mangleName(gBaseInfoGetName( if WriteFields or callerAlloc.contains(($gBaseInfoGetNamespace(info)).toLowerAscii & '.' & mangleName(gBaseInfoGetName(
info))) or info))) or
mangleName(gBaseInfoGetName(info)) == "TargetEntry" or mangleName(gBaseInfoGetName(info)) == "TargetEntry" or
["gst.Message", "gst.MiniObject", "glib.List"].contains(($gBaseInfoGetNamespace(info)).toLowerAscii & '.' & mangleName( ["gst.Message", "gst.MiniObject", "glib.List", "pango.Attribute", "nice.Address", "nice.Candidate"].contains((
gBaseInfoGetName(info))): $gBaseInfoGetNamespace(info)).toLowerAscii & '.' & mangleName(gBaseInfoGetName(info))):
if gBaseInfoGetName(info) == "Address":
echo "kkk", info.gStructInfoGetNFields()
for j in 0.cint ..< info.gStructInfoGetNFields(): for j in 0.cint ..< info.gStructInfoGetNFields():
let field = info.gStructInfoGetField(j) let field = info.gStructInfoGetField(j)
let name = manglename(gBaseInfoGetName(field)) & EM let name = manglename(gBaseInfoGetName(field)) & EM
@ -2053,6 +2138,11 @@ proc writeStruct(info: GIStructInfo) =
if mangleName(gBaseInfoGetName(info)) == "List" and moduleNamespace == "glib": if mangleName(gBaseInfoGetName(info)) == "List" and moduleNamespace == "glib":
output.writeLine("\nproc g_list_free*(list: ptr glib.List) {.importc: \"g_list_free\", libprag.}") output.writeLine("\nproc g_list_free*(list: ptr glib.List) {.importc: \"g_list_free\", libprag.}")
output.writeLine("\nproc g_list_prepend*(list: ptr glib.List; data: pointer): ptr glib.List {.importc: \"g_list_prepend\", libprag.}") output.writeLine("\nproc g_list_prepend*(list: ptr glib.List; data: pointer): ptr glib.List {.importc: \"g_list_prepend\", libprag.}")
if mangleName(gBaseInfoGetName(info)) == "SList" and moduleNamespace == "glib":
output.writeLine("\nproc g_slist_free*(list: ptr glib.SList) {.importc: \"g_slist_free\", libprag.}")
output.writeLine("\nproc g_slist_prepend*(list: ptr glib.SList; data: pointer): ptr glib.SList {.importc: \"g_slist_prepend\", libprag.}")
if mangleName(gBaseInfoGetName(info)) == "TargetEntry": if mangleName(gBaseInfoGetName(info)) == "TargetEntry":
# assert false executed for gtk3 # assert false executed for gtk3
output.writeLine(TargetEntryProx) output.writeLine(TargetEntryProx)
@ -2066,7 +2156,9 @@ proc writeStruct(info: GIStructInfo) =
genDestroyFreeUnref() genDestroyFreeUnref()
template writeSignal() = template writeSignal() =
#echo gBaseInfoGetName(signalInfo), gCallableInfoGetNArgs(signalInfo), gTypeInfoGetTag(zzzu)
if gCallableInfoGetNArgs(signalInfo) > 0 or gTypeInfoGetTag(zzzu) != GITypeTag.VOID: if gCallableInfoGetNArgs(signalInfo) > 0 or gTypeInfoGetTag(zzzu) != GITypeTag.VOID:
#echo "aaa", gBaseInfoGetName(signalInfo)
var memo = "" var memo = ""
memo.addInt(gCallableInfoGetNArgs(signalInfo)) memo.addInt(gCallableInfoGetNArgs(signalInfo))
memo.add(RecSep) memo.add(RecSep)
@ -2109,6 +2201,9 @@ template writeSignal() =
signalbuffer.write(" g_signal_connect_data(self.impl, \"") signalbuffer.write(" g_signal_connect_data(self.impl, \"")
signalbuffer.write($gBaseInfoGetName(signalInfo)) signalbuffer.write($gBaseInfoGetName(signalInfo))
signalbuffer.writeLine("\", cast[GCallback](p), xdata, nil, cf)") signalbuffer.writeLine("\", cast[GCallback](p), xdata, nil, cf)")
if false: #gBaseInfoGetName(signalInfo) == "invalidate-size":
echo "aha"
signalbuffer.writeLine("aha")
proc writeInterface(info: GIInterfaceInfo) = proc writeInterface(info: GIInterfaceInfo) =
if not suppressType: if not suppressType:
@ -2467,6 +2562,10 @@ proc writeConst(info: GIConstantInfo) =
var h = $gBaseInfoGetName(info) var h = $gBaseInfoGetName(info)
if h == "MODIFIER_MASK": if h == "MODIFIER_MASK":
return return
if h == "ATTR_INDEX_FROM_TEXT_BEGINNING":
return # gint in gir, but should be guint
if h.replace("_", "").toLowerAscii == "keych": if h.replace("_", "").toLowerAscii == "keych":
h.add(str[^5]) h.add(str[^5])
if gdkKeys.contains(h.toLowerAscii): if gdkKeys.contains(h.toLowerAscii):
@ -2692,7 +2791,35 @@ proc glistObjects2seq*[T](t: typedesc[T]; l: ptr glib.List; elTransferFull: bool
g_object_set_qdata(r.impl, Quark, addr(r[])) g_object_set_qdata(r.impl, Quark, addr(r[]))
el = el.next el = el.next
proc glistStructs2seq*[T](l: ptr glib.List): seq[T] = proc gslistObjects2seq*[T](t: typedesc[T]; l: ptr glib.SList; elTransferFull: bool): seq[T] =
var r: T
var obj: ptr Object00
var el = l
while el != nil:
obj = cast[ptr Object00](el.data)
if obj == nil:
continue
let qdata = g_object_get_qdata(obj, Quark)
if qdata != nil:
r = cast[T](qdata)
assert(r.impl == obj)
result.add(r)
else:
fnew(r, finalizeGObject)#" % [mprefix])
r.impl = obj
GC_ref(r)
if elTransferFull:
if g_object_is_floating(r.impl).int != 0:
discard g_object_ref_sink(r.impl)
else:
discard g_object_ref_sink(r.impl)
g_object_add_toggle_ref(r.impl, toggleNotify, addr(r[]))
g_object_unref(r.impl)
assert(g_object_get_qdata(r.impl, Quark) == nil)
g_object_set_qdata(r.impl, Quark, addr(r[]))
el = el.next
proc glistStructs2seq*[T](l: ptr glib.List; igFin: bool): seq[T] =
var r: T var r: T
var el = l var el = l
while el != nil: while el != nil:
@ -2700,6 +2827,20 @@ proc glistStructs2seq*[T](l: ptr glib.List): seq[T] =
continue continue
newWithFinalizer(r) newWithFinalizer(r)
r.impl = cast[typeof(r.impl)](el.data) r.impl = cast[typeof(r.impl)](el.data)
r.ignoreFinalizer = igFin
result.add(r)
el = el.next
proc gslistStructs2seq*[T](l: ptr glib.SList; igFin: bool): seq[T] =
var r: T
var el = l
while el != nil:
if el.data == nil:
continue
newWithFinalizer(r)
r.impl = cast[typeof(r.impl)](el.data)
r.ignoreFinalizer = igFin
result.add(r)
el = el.next el = el.next
#proc seq2GList*[T](s: seq[T]): ptr glib.List = #proc seq2GList*[T](s: seq[T]): ptr glib.List =
@ -2720,7 +2861,21 @@ proc seq2GList*[T](s: seq[T]): ptr glib.List =
discard # make it compile for these cases, of course will not work discard # make it compile for these cases, of course will not work
return l return l
proc seq2GSList*[T](s: seq[T]): ptr glib.SList =
var l: ptr glib.SList
var i = s.len
while i > 0:
dec(i)
when T is gobject.Object: # or T is pango.Item:
l = g_slist_prepend(l, s[i].impl)
elif T is cstring:
l = g_slist_prepend(l, s[i]) # cstring
else:
discard # make it compile for these cases, of course will not work
return l
#include gimplgobj #include gimplgobj
include gimplglib
""" """
@ -2743,13 +2898,40 @@ proc fromStringStructure*(string: cstring; `end`: var string): Structure =
""" """
const Pango_EPI = """
when not declared(ATTR_INDEX_TO_TEXT_END):
const ATTR_INDEX_TO_TEXT_END* = uint32.high
when not declared(ATTR_INDEX_FROM_TEXT_BEGINNING):
const ATTR_INDEX_FROM_TEXT_BEGINNING* = uint32.low
proc setStartIndex*(a: Attribute; i: uint32) =
a.impl.startIndex = i
proc setEndIndex*(a: Attribute; i: uint32) =
a.impl.endIndex = i
proc setIndices*(a: Attribute; s, e: uint32) =
a.impl.startIndex = s
a.impl.endIndex = e
proc `startIndex=`*(a: Attribute; i: uint32) =
a.impl.startIndex = i
proc `endIndex=`*(a: Attribute; i: uint32) =
a.impl.endIndex = i
#proc `indices=`*(a: Attribute; s, e: uint32) =
proc `indices=`*(a: Attribute; se: tuple[start: uint32; `end`: uint32]) =
a.impl.startIndex = se[0]
a.impl.endIndex = se[1]
"""
const Nice_EPI = """ const Nice_EPI = """
proc nice_agent_attach_recv*(self: ptr Object00; streamID: cuint; componentID: cuint; ctx: ptr glib.MainContext00; include gimplnice
fn: AgentRecvFunc; data: pointer): gboolean {.importc, libprag.}
proc attachRecv*(self: Agent; streamID: int; componentID: int; ctx: MainContext; fn: AgentRecvFunc; data: pointer): bool =
toBool(nice_agent_attach_recv(self.impl, streamID.cuint, componentID.cuint, ctx.impl, fn, data))
""" """
@ -2762,6 +2944,8 @@ when not declared(g_thread_new):
fnew(result, finalizerunref) fnew(result, finalizerunref)
result.impl = g_thread_new(name, fn, data) result.impl = g_thread_new(name, fn, data)
#include gimplglib will not work as glib does not import gobject
""" """
const GTK3_EPI = """ const GTK3_EPI = """
@ -3047,6 +3231,10 @@ proc cstringArrayToSeq*(s: ptr cstring): seq[string] =
output.writeLine("import glib\n") output.writeLine("import glib\n")
elif namespace == "Gst": elif namespace == "Gst":
discard discard
elif namespace == "Nice":
output.writeLine("import macros, nativesockets\n")
output.writeLine("from strutils import `%`\n")
discard
if namespace == "GObject": if namespace == "GObject":
output.writeLine("\nimport times") output.writeLine("\nimport times")
output.writeLine("var qt = \"NGIQ\" & $epochTime()") output.writeLine("var qt = \"NGIQ\" & $epochTime()")
@ -3342,7 +3530,9 @@ proc get$1*(builder: Builder; name: string): $1 =
fnew(result, $3.finalizeGObject) fnew(result, $3.finalizeGObject)
result.impl = gobj result.impl = gobj
result.ignoreFinalizer = true result.ignoreFinalizer = true
g_object_add_toggle_ref(result.impl, toggleNotify, addr(result[])) GC_ref(result) # as following g_object_unref() call will also execute a GC_unref(result) call
g_object_add_toggle_ref(result.impl, toggleNotify, addr(result[])) # do toggle_refs make sense at all for builder objects?
g_object_unref(result.impl) # new for v0.8.4 and GTK4, make close main windows working
assert(g_object_get_qdata(result.impl, Quark) == nil) assert(g_object_get_qdata(result.impl, Quark) == nil)
g_object_set_qdata(result.impl, Quark, addr(result[])) g_object_set_qdata(result.impl, Quark, addr(result[]))
assert(toBool(g_type_check_instance_is_a(cast[ptr TypeInstance00](result.impl), gt))) assert(toBool(g_type_check_instance_is_a(cast[ptr TypeInstance00](result.impl), gt)))
@ -3359,6 +3549,9 @@ proc get$1*(builder: Builder; name: string): $1 =
if namespace == "Gst": if namespace == "Gst":
output.write(Gst_EPI) output.write(Gst_EPI)
if namespace == "Pango":
output.write(Pango_EPI)
if namespace == "GLib": if namespace == "GLib":
output.write(GLib_EPI) output.write(GLib_EPI)
@ -3522,7 +3715,7 @@ launch()
# if not xcallerAlloc.contains(el): # if not xcallerAlloc.contains(el):
# echo el # echo el
# 3525 lines # 3717 lines
# gtk_icon_view_get_tooltip_context bug Candidate # gtk_icon_view_get_tooltip_context bug Candidate
# gtk_tree_view_get_cursor bug # gtk_tree_view_get_cursor bug
# #