Welcome to the first in a series of posts intended to explain the ExeSketch code I've written up to this point. As lining up the beginning of a retelling with the corresponding point in temporal space seems logical, I'll begin with the EventHandler and Event classes, which (odd though it may seem) were the first things I coded.
In truth, the event system in place wasn't made specifically for ExeSketch - initially it was just an idea I had for getting around GLUT's way of dealing with events. Having spent quite some time coding in PyGame, I've become accustomed to an event loop in which I first procure a list of all the new events with pygame.event.get(), then process them one by one, sending each one to the appropriate point in the code. In this way all the events are handled by one central function (normally just in the mainloop), which I find relatively simple to maintain. However, in GLUT, there is no user-defined mainloop - the code you write is entirely driven by GLUT events, and the way you get your code into the program's mainloop is by writing it into special functions and binding them to GLUT events.
This is actually fairly simple - to take the window repainting event as an example, you begin by writing a function RedrawScreen() (or whatever you choose to name it) and bind it to the GLUT call to redraw with glutDisplayFunc(RedrawScreen). And this works rather well for situations similar to the above. However, this system of stuffing code into designated orifices isn't particularly helpful for user events that need to be passed to several different objects, as for a fairly average application you would need to register glutMouseFunc, glutMotionFunc, glutPassiveMotionFunc, glutKeyboardFunc and glutSpecialFunc and then have every one send its events to all the appropriate objects with its own version of the event handling code. Each object would then have to have specific functions for each type of event, as each event has different arguments - mouse position, key, whether the buttom is going in or out - making event handling a process liable to wear your CTRL and C-keys out with the haste and enthusiasm of an industrial belt sander.
Being rather fond of the aforementioned keys (they really come in handy when you're writing an essay on something well-documented by Wikipedia...), I decided an alternative had to be found. And when, after much pondering, this alternative eventually came, it did so in the shape of the ExeSketch EventHandler class.
To put it simply, each registered glutFunkyFunc creates an object of type Event, which stores all the appropriate information for the event, and then passes that to a central EventHandler object. On a call to EventHandler::ProcessQueue(), any events that may have accumulated in the event handler's buffer are processed by the much-coveted single event-handling function, which does what it needs to the events before sending them off to all the objects registered with it.
However, as mentioned before, different events have different parameters supplied with them. To resolve the problems this would cause, I created an enumeration of all possible user events, which you can see here. Essentially it is written as a branched tree: At the root is EVENT, which splits into MOUSE and KEYBOARD. MOUSE then splits into UP (button up), DOWN (button down), and MOTION, with UP and DOWN splitting further into individual buttoms. KEYBOARD splits into all the possible key events, each preceded by K_. An item in this imaginary tree structure is translated into a variable name by joining its branches with underscores, for example EVENT_MOUSE_DOWN_LEFT (when the user left-clicks) or EVENT_KEYBOARD_K_S (when the 's' key is pressed). The enumeration simply defines these as numerical constants, and so they can be used to define an event type throughout the code. An Event object stores its event type as the appropriate integer, as well as two other integers: xpos and ypos. These are used to store the mouse position parameters for mouse functions, and when not needed are simply set to the integer constant NA.
Meanwhile, the EventHandler class stores a collection of these objects in an STL queue, chosen because it provides basic FIFO queue functionality and no more, which is all that is needed in this case (having nifty functions to swap/juggle/tightrope across individual items in the container would be slightly overkill). It holds a pointer to the ObjectDisplay class (to be explained in a later post) and an STL vector of pointers to ObjectManager objects (again, to be explained in a later post), to which it sends events. ObjectManagers are registered with it using a RegisterClient function which accepts the pointer to register.
The ObjectManager and ObjectDisplay objects which receive the events need only provide a HandleEvent function to deal with the Event objects the event handler sends them, as well as making themselves known with the event handler's RegisterClient function. From then on, control over user input is entirely given over to the EventHandler.
You'll have to excuse the boundless prolixity of this post, I didn't really intend for it to end up as long as this. It was alyways going to be the longest in the series anyway; the next ones should be shorter. If you stuck with it as far as here, congratulations! You have a God-like attention span. To the rest of you, I'd like to take this opportunity to recommend a nifty online utility I found a short while ago for reading long things incredibly quickly - Spreeder. Essentially you copy the text to read into the box, and it displays it word by word at several hundred words per minute (you can set the speed to whatever is readable). By this approach you avoid problems like being slowed down by line breaks, unintentionally casting your eyes backwards along a line and the dreaded subvocalisation, which means that you get through the text a lot faster whilst still retaining almost as much as you would normally.
It's useful for quickly digesting blog posts written by people who need to learn the art of brevity.
No comments:
Post a Comment