UXEvent
UXEvent is one UI event, in neutral terms. Each driver’s nextEvent decodes
the backend’s native event (a GEM message, a Win32 MSG, an NSEvent, a DOM
event) into one of these, so the run loop and everything above it name no
platform.
#use <UXKit> // or #import "UXEvent.xc"Overview
Section titled “Overview”class UXEvent { u8 kind; // UXEventKind i16 x, y; // where, in the window's coordinates u16 buttons; u16 key; // (scancode << 8) | ascii u16 modifiers; i32 handle; // which window a system event refers to i32 a, b; // decoded payload, per kind Object* data; // optional payload: an UXIndexSet, a String}Most programs never build one. Events arrive, the window routes them, and you see the result as a callback firing or a delegate method running. Read this page to learn what a kind means when you tap the stream or write a driver.
The fields are flat and mostly integers. An event is copied, recorded and
replayed, and a structure that owns nothing can be kept by a recorder without
keeping other objects alive. The one exception is
data.
The kinds
Section titled “The kinds”| kind | meaning |
|---|---|
UXEventNone | nothing happened (a timed-out wait) |
UXEventMouseDown | a press, at x, y |
UXEventMouseUp | a release |
UXEventMouseDragged | movement with a button held |
UXEventKeyDown | a key, in key |
UXEventWheel | a wheel notch over a client-drawn scroll region; a is the notch count |
From the window system
Section titled “From the window system”| kind | meaning |
|---|---|
UXEventRedraw | repaint; handle says which window |
UXEventClose | the close box; handle says which window |
UXEventResize | the window was resized |
UXEventMove | the window was moved |
UXEventMenuSelect | a menu item was chosen; a is the title object, b the item |
UXEventQuit | the application should end |
Outcomes, not input
Section titled “Outcomes, not input”Three kinds can look redundant, but each has a specific purpose:
| kind | meaning |
|---|---|
UXEventScrolled | a scroll view came to rest; a its node, b the new offset |
UXEventSelected | a table’s selection settled; a its node, b the anchor row, data an UXIndexSet of every selected row |
UXEventTextChanged | a native field’s contents changed; a its node, data a String |
These announce what happened, not what the user did, and for certain interactions they are the only trace.
- Dragging a scrollbar runs inside the driver’s modal
trackDragStep, which emits no events. A recorder would capture the initial press and nothing else, then replay it and leave the view scrolled to a different position. - On a backend whose table is a native list, a click never reaches the toolkit.
It arrives as
WM_NOTIFY/LVN_ITEMCHANGED. - Keystrokes in a native text field go straight to the
EDITorNSTextFieldand surface only as “the text is now this”.
In each case the input cannot be recorded, so the toolkit records the outcome
instead. This lets UXEventRecorder
work across backends that handle input in completely different ways.
Keys and modifiers
Section titled “Keys and modifiers”GEM hands over key = (scancode << 8) | ascii, and the toolkit keeps that shape
on every backend:
#define UX_KEY_ASCII $00FF // mask for the character#define UX_KEY_TAB $09#define UX_KEY_RETURN $0Du16 ch = e.key & UX_KEY_ASCII; // the characteru16 sc = e.key >> 8; // the scancode, for keys with no characterModifiers follow evnt_multi’s kstate:
#define UX_MOD_SHIFT $03 // either shift#define UX_MOD_CTRL $04if ((e.modifiers & UX_MOD_CTRL) != 0) { /* ctrl was held */ }Carrying more than two integers
Section titled “Carrying more than two integers”Object* dataSome events do not fit in a and b. A table’s selection is a set of rows
(an anchor alone would replay as a single row however many were chosen), and a
field’s contents are a string.
data is whatever that kind documents: an
UXIndexSet for a selection, a String for
text. A recorder copies the reference, so a captured event keeps its payload.
Tapping the stream
Section titled “Tapping the stream”callback gEventTap void(UXEvent* e); // set via UXApplication.setEventTapA tee on every event, used by the recorder and by tests that need a clock.
It lives on the event module, not on
UXApplication, and the placement
matters. On backends whose controls are native, a click becomes BN_CLICKED or
an NSButton action and is turned straight into ctl.mouseDown without
passing through the application’s dispatch. A tap that only watched
dispatchEvent would see everything on GEM and nothing on Windows or macOS.
Drivers announce such clicks here, at their coordinates, so they can be
recorded, and replaying one goes back through the ordinary hit test to the same
control.
The tap is a tee, not a filter: it is called before dispatch and must not consume the event.
Replay
Section titled “Replay”bool gInputReplay; // true while a recording is being replayedWhile this is set, trackDragStep reports “not dragging” immediately.
This prevents the modal drag problem. A replayed press on a scrollbar would re-enter the driver’s modal loop, which tracks the live mouse, so the widget would keep scrolling under the user’s hand after the replay finished. With the flag set, a replayed press does the press and nothing more.
Example
Section titled “Example”Watching the stream with a tap, the only way to see every event on every backend:
class Watcher : Object { i32 presses; void init(void) { presses = 0; }
void onEvent(UXEvent* e) { if (e.kind == UXEventMouseDown) { presses = presses + 1; Stdio.printf("press %d at %d,%d\n", presses, e.x, e.y); } else if (e.kind == UXEventSelected) { Stdio.printf("table %d selection settled, anchor row %d\n", e.a, e.b); } }}
Watcher* w = new Watcher();app.setEventTap(&w.onEvent);Topics
Section titled “Topics”void init(void)Zeroes every field and sets kind to UXEventNone. Drivers reuse a single
event object across the loop instead of allocating per event, so init is
called before each fill. A stale data pointer surviving into the next event
would carry a payload from the wrong interaction.
Conforms to
Section titled “Conforms to”- Inherits
Object
See also
Section titled “See also”UXApplication: the run loop that dispatches these, andsetEventTapUXWindow: routes a mouse event to a view by hit testUXEventRecorder: records and replays the streamUXIndexSet: the payload of a selection