Event Handling in the ARDOUR GUI

Mouse Events (and sometimes keyboard events)

  1. underlying window system delivers event to GDK
  2. GDK delivers to GTK
  3. if the canvas is the target of the event (e.g. due to mouse pointer position and/or focus), GTK delivers the event to the canvas widget (GtkCanvas)
  4. The canvas delivers the event to the current item on the canvas.

    The current item is updated when motion events arrive, or when items are deleted, or when an item grabs "canvas focus".

  5. Items emit their Event PBD::Signal. Note that unlike GTK, Canvas items do not have 1 signal per event type, but just a single signal used for any event.
  6. The parent object of the item (eg. a RegionView) will (generally) have connected a callback/handler to the signal, and these generally end up forwarding the event to the Editor via its typed_event method, along with a pointer to the item that received the event and an item type enum.
  7. typed_event() performs a switch on the event type, and calls the relevant Editor method e.g. ::button_press_handler.
  8. That method performs a switch on the item type, and dispatches appropriately. The "final" dispatch may be to an Editor method, or an item owner method, or both, or anything else.

    A common case (for button press events) is to create a new Drag-derived object and make that active. Another common case (for button release events) is to tell the item to create and display a context menu given the item/item type.

  9. Normal dispatch termination rules generally apply: if a handler returns true, event propagation will stop; if it returns true, the event will propagate to a higher level item/widget.

Keyboard Events

Keyboard events are handled via a somewhat different mechanism. GTK delivers keyboard events to the top level window that has keyboard focus, allowing the window to have its own internal focus (i.e. on a given widget in that window). For anything other than simple dialogs that can/should handle any key events while they are visible, we set up a handler for top level windows "key event" signals that essentially passes them to ARDOUR_UI::key_event_handler. A few windows will explicitly do this using ARDOUR_UI_UTILS::relay_key_press().

The key event handler plays a pivotal role in fundamentally changing how keyboard events are handled in Ardour, but most of that work is done in ARDOUR_UI::key_press_focus_accelerator_handler(). There are ample comments in that method to explain what it does, but at a high level its job is to decide between using a "binding" (an internal Ardour concept) (if one exists) before or after allow GTK's normal event propagation and keyboard focus to do their thing (and potentially handle the event).

We do this because GTK's concept of keyboard accelerators doesn't suit our purposes in many important ways, so we needed to add our own mechanism. In some cases, when the user presses a key and there is a binding for it, we want the binding to be used in preference to any normal GTK keyboard handling stuff (e.g. text entry). In other cases, it is important for the normal keyboard handling stuff to operate first, and only if that does not handle the event to consider using bindings.

Note that if the keyboard event is not handled by a binding, and it propagates into the normal GTK mechanism, if destined for a canvas, it will end up flowing through the enumerated process described above ("Mouse events...").

Note that we also leverage GTK's "keyboard snooper" mechanism to check keyboard events before the rest of GTK sees them. This allows us to keep track of some global keyboard state. It also allows us to handle a few keys in a truly global way, such as Primary-w. Generally, the snooper does not claim to have handled the event, so it will flow into the mechanism described above.