Tuesday, August 13, 2013

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).

I'm holding off on putting anything up on the Github repository until I get a few more portions fleshed out and hopefully at least a minimal example application running.  Pushed some early drafts up for review, please see: https://github.com/Omnivore61/Angora.


As always, I welcome any and all feedback, thanks!

Omnivore

Monday, August 12, 2013

Angora

A GUI framework implemented as a concurrent window system in Go.


Building upon the ideas presented in the white paper "A Concurrent Window System" by Rob Pike (1989?), Angora is an attempt to update the model and create a GUI framework based upon it for use in current Go desktop applications running on popular operating systems.  One significant departure from the ACWS model is a re-examination of the role of events.

When the ACWS paper was written, graphical user interfaces were barely out of the lab.  The user base of GUI's today is at least a million times larger than it was then and the nature of the user population is very different.  There are no clean separations to be made between keyboard, mouse, touch-screen, voice recognition, game controllers, and other human input devices.  There are, however, two basic forms of human input; namely data and control.  Data input is the entering of some form of data to be processed, stored, sent; in some way used by the process.  Control input is input which controls the process, including control input which manipulates data or data flow.  It is, in simple terms, the difference between retyping a paragraph and using cut and paste.

Given present operating systems, an event system is already tied to at least the main window of an application.  There is no choice at the GUI framework level of whether or not to work with a platform supplied event stream.  The most common model in use today ties together the attributes of a window, a main entry point, and a main event message handler all in one lump and largely constrains it to conducting at least display operations in one thread of execution.  Simplifying, the Angora uses a  model which views the interface with a platform's windowing system as three distinct interfaces; event input, event output, and display output.  A further simplification is to treat all three interfaces as being non-reentrant and tied to a specific 'main' thread.

Angora considers each of these interfaces to be a separate channel of communications.  These channels are then subdivided further.  For example, Angora subdivides the event input channel into a supervisory input channel, a data input channel, and a process control channel.  The output event channel is fairly simple, just enough to meet the requirements of the underlying system, mainly movement and state related.  The display output channel is potentially much more complex and seemingly a bottleneck for concurrency, or is it?

What if the display output channel is treated simply as a data pipe that provides and updates the minimum data necessary for a presentation?  Regardless of the actual implementation of the display interface, a window in Angora need only supply enough data to initialize the representation and then to notify the representer of any changes to the representation.  The presentation layer is entirely decoupled from the window except for that single channel.  The presentation layer could be as simple as a bitmap renderer, as complex as an OpenGL text renderer, or even as involved as DX11 animated model rendering engine.

Revisiting input events for a moment.  While the desirable simplicity of the ACWS paper's K(keyboard) and M(mouse) do not seem to be directly achievable, at least in the Angora model, there is an approach by which some of that simplicity and directness may be recovered.  If the event input channel is viewed as a pipe, it is conceivable to provide filters and multiplexers to that pipe.  The initial ones are assumed to be embedded in the interface to the platform and provide the separation of supervisory, data input, and process control channels.  Specialized filters could then be created to further subdivide those, simplifying the implementation of various child windows or widgets.

At this point, the reader is encouraged to take a break and revisit Rob Pike's "A Concurrent Window System" white paper.

Status:

Currently under design.