Electric Froth

______ _ ______ ( / // _/_ o ( / _/_ / /-- // _ _, / _ , _, -/--_ __ / /_ (/____/(/_(/_(__(__/ (_(_(__ _/ / (_(_)(__/ /_


Nim! pt5

by Joel Kleier on 2014-11-06

In my last post I showed how to get a circle bouncing back and forth on the screen. Next, I’d need to figure out how Nim handles SDL_Event, which normally uses a union structure along with a type field to determine how the data for an event gets used.

Nim doesn’t have a union type as far as I can tell. It has a pragma for telling the compiler that a Nim structure should be translated to a C union, but that’s not what I"m looking for.

Digging into the SDL2 wrapper a little, I think this is the piece of code I need to understand:

template EvConv(name, name2, ptype: expr; valid: set[TEventType]): stmt {.immediate.}=
  proc `name`* (event: var TEvent): ptype =
    assert event.kind in valid
    return cast[ptype](addr event)
  proc `name2`* (event: var TEvent): ptype =
    assert event.kind in valid
    return cast[ptype](addr event)

which is used like:

EvConv(EvWindow, window, PWindowEvent, {WindowEvent})

What is a template in Nim’s parlance? As it turns out, it’s a way of creating a simple substitution macro at compile time, and is called like a normal procedure.

Therefore the EvConv template being defined above is creating 2 procedures that are identical, except for the name. If you direct your gaze to the usage example, the first two parameters are EvWindow and window — which means that the example will result in two procedures called EvWindow and window, which do precisly the same thing, which is to make certain the kind of event is a valid kind, and then to cast (reinterpret the type without altering the bit structure) the event data into the Nim type pointed to by the ptype pointer.

The result of this is that in my program I can check for a keydown event thusly:

    if evt.kind == KeyDown:
      if evt.key.keysym.sym == K_ESCAPE:
        runGame = false

There’s a few things that initially tripped me up with even that, so allow me to explain a few more things:

  1. the TEvent type (in the SDL2 wrapper) is an unsafe/untraced pointer — but it get’s dereferenced implicitely by the “.”

  2. the EvConv template produces the EvKeyboard and key procedures that each can be used in the method call syntax style on TEvent values

  3. The actual key value is retrieved from the TEvent property keysym, which in turn is a TKeySym type. The TKeySym type has properties that get values for a scancode, a keycode, or the unicode value. The difference between the scancode and keycode is that the scancode tries to represent the location on the actual keyboard, whereas the keycode is just the value returned by the device, regardless of whether or not it’s in the expected physical position. I chose keycode (the .sym call) because the scancode seems like it’s rife with potential hardware compatibility issues, and, eventually, I should allow for custom remapping of keys anyway.


That still wasn’t much code, but I did learn a lot about the SDL2 wrapper, Nim, and even a little about SDL2 itself!