I'm definitely doing something wrong, trying to use xlib wrapper, because nimrod code looks ugly. This is the original C source:
while(1) {
XNextEvent(dpy, &e);
if(e.type==Expose && e.xexpose.count<1) {
XDrawString(dpy, win, gc, 10, 10, "Hello World!", 12);
paint(cs);
}
else if(e.type==ButtonPress) break;
else if(e.type==ClientMessage && e.xclient.data.l[0] == wmDeleteMessage) {
break;
}
}
This is how I translated it into Nimrod:
while true:
discard XNextEvent(dpy, addr(e))
if e.theType == Expose and cast[PXExposeEvent](e.addr).count < 1:
discard XDrawString(dpy, win , gc, 10, 10, "Hello World!", 12)
paint(cs)
elif e.theType == ButtonPress:
break
elif e.theType == ClientMessage and
cast[ptr cint]((cast[PXClientMessageEvent](e.addr).data.addr
))[] == cast[cint](wmDeleteMessage):
break
Awaiting your suggestions on how to improve the code in nimrod way.
Since the original code doesn't check return values of XNextEvent() and others you could add the {.discardable.} pragma to them to avoid having to write it explicitly in the calling code.
I know nothing of xlib and its types, but it seems that checking for the ClientMessage type and extracting the first entry of that nested array is sort of predefined. I would wrap that code in something similar to:
proc ClientMessage(e: TypeOfE): probablyEnumType =
foo
Where foo would be a check of e.theType to be ClientMessage and then you apply the casts. With the advantage of adding runtime checks or maybe a when release for quick access and otherwise raise errors if something goes wrong, like the array not being there or whatever you think is a common error. Then your later code would look similar to:
elif e.ClientMessage == wmDeleteMessage:
bar
Not sure if the ClientMessage proc would collision with the type, maybe find a slightly different name/convention for such wrapping procs. And maybe make them a template for speed.Most likely many types of Nimrod's xlib wrapper are inconvenient to work with. Please fix them instead of hacking around. That said unions are always ugly to access. Best practice is to define some templates that hide the messy details:
template checkEv(ev, val: expr, body: stmt) {.immediate, dirty.} =
if ev.theType == val:
let ev = cast[`PX val Event`](addr(ev))
body
checkEv(e, Expose):
if e.count < 1:
discard XDrawString(dpy, win, gc, 10, 10, "Hello World!", 12)
In the CSFML binding, for TEvent, I was able to use a type like
type
TEvent {.pure.} = object
case kind*: TEventKind
of MouseButtonDown, MouseButtonUp: button: TMouseButtonEvt
etc
However, I tried to do this with SDL's TEvent and was unsuccessful because SDL_Event's alignment is "packed" (IIRC)
Good luck
@Araq, that kind of template significantly changes code, i think the following although unsafe would be better:
proc xclient*(e: PXEvent): PXClientMessageEvent =
## Treats XEvent as XClientMessageEvent
return cast[PXClientMessageEvent](e)
proc xclient*(e: var TXEvent): PXClientMessageEvent =
return xclient(PXEvent(e.addr))
BTW is there a better way to treat both var and pointer type the same here, in order to have only one such proc?
Then the user code resembles the original C code:
else if(e.type==ClientMessage && e.xclient.data.l[0] == wmDeleteMessage) {
break;
}
Translated into nimrod:
elif e.theType == ClientMessage and
cast[PAtom](e.xclient.data[0].addr)[] == wmDeleteMessage:
break
Shrug your way is fine too; whatever accomplishes your goal. It'll always be ugly because xlib is ugly.
Regarding ptr vs. var: It's yet-another planned feature to offer auto-deref so only the var version would be necessary. For now you need both or define a converter.