Day 2: Concurrent event handling
Before digging in too deep, I wanted to get an peek into the future of what event handler code would look like. Last thing I want to do is to create something harder to use than ye old big ugly switch statement! The following code snippet shows what I believe the format of most event handlers will look like.
// Binding for a single key/mouse action enabled event
go someWidget.ControlHandler(
app.GetControlEventChannel(someWidget.ID),
app.GetEventBinding("DoSomethingCrazy"),
func(evt ControlInfo) {
// handle event here
})
// Binding for multiple keys/mouse actions to do an event
go someWidget.ControlMultiHandler(
app.GetControlEventChannel(someWidget.ID),
app.GetEventMultiBinding("DoSomethingEvenMoreCrazy"),
func(evt ControlInfo) {
// handle event here
})
The above would be for handling control input events. Like arrow keys, tab focus selection, mouse clicks, etc. Control channels are multiplexed a bit more than I'd like, but it seems a fairly clean separation from pure Unicode text input and easy enough to deal with. The handler functions being called above do filtering of the channel and upon encountering a valid trigger, pass the user written handler along with the event data, to a worker channel inside the widget being controlled. This provides clean, mutex free, integrity of any data stored inside the widget.
The output channels are very simple. One, is the 'RemoveEvent' channel used by widgets (or their parent in case of error) to remove themselves from Angora's internal bookkeeping structures as well as closing channels. The second, while simple, is much further reaching; it has the following, deceptively simple, form:
// Presentation Output channel
type Presenter interface {
ID() int
Paint(context *GraphicsContext) error
}
type PresentEvent chan Presenter
Upon receipt of a resize command over the control input channel, or whenever a widget or its parent detects a state change requiring a change in the display, the widget sends an structure satisfying the contract implied in the interface to the Angora kernel. At the appropriate times, the Angora kernel will invoke the object's Paint method after setting up the appropriate GraphicsContext. The object (or structure/code) provided by a widget to implement the Presenter interface is necessarily dependent upon the nature of the GraphicsContext used. However, since it should only have a copy of state information for thread safety, it should be separate enough that supporting future GraphicsContext interfaces should be less painful than normal.
At the present time, the backend graphics subsystem is OpenGL based. Indeed, the underlying interface to the operating system is through the use of GLWF3 and OpenGL 2.1 running in non-immediate mode (as close as possible to modern GL3.x but still able to run on older platforms). Hopefully this combination of technologies will result in a highly portable solution. Another benefit to this approach is that it is perfectly capable of being a GUI for an OpenGL game (conceptually at least someday).
As always, I welcome any and all feedback, thanks!
Omnivore