fixes and example for libnice
This commit is contained in:
parent
f03791c714
commit
26a6b751b2
5 changed files with 700 additions and 25 deletions
310
examples/gtk3/simple_example.nim
Normal file
310
examples/gtk3/simple_example.nim
Normal 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
67
gintro/gimplglib.nim
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
105
gintro/gimplnice.nim
Normal 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
|
||||||
|
|
||||||
|
|
||||||
235
tests/gen.nim
235
tests/gen.nim
|
|
@ -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,6 +1470,7 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo) =
|
||||||
if itt == 2:
|
if itt == 2:
|
||||||
depStr = " {.deprecated.}"
|
depStr = " {.deprecated.}"
|
||||||
methodBuffer.writeLine(hhh & depStr & " =")
|
methodBuffer.writeLine(hhh & depStr & " =")
|
||||||
|
if not isGObject:
|
||||||
methodBuffer.write(pars.tempRes) # caution, gobjectTemp() may write tempRes too.
|
methodBuffer.write(pars.tempRes) # caution, gobjectTemp() may write tempRes too.
|
||||||
methodBuffer.write(pars.arrLex)
|
methodBuffer.write(pars.arrLex)
|
||||||
methodBuffer.write(sss)
|
methodBuffer.write(sss)
|
||||||
|
|
@ -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
|
||||||
#
|
#
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue