Files
sibo-playground/docs/reference/06-ui.md
T

24 KiB
Raw Blame History

Psion SIBO / Workabout MX — User-Interface Programming

The Window Server and the Console

Reference notes drawn from the SIBO 'C' SDK Window Server Reference (v2.30, March 1999) and the PLIB Reference (console functions). Everything below is sourced from those two manuals; section/line references are given as [wserv] and [plib]. Anything not backed by the manuals is explicitly flagged as reverse-engineered or uncertain.

Machine-type note. The manuals cover the HC, MC, S3, S3a and Workabout. The Workabout is reported by the window server as machine type WS_TYPE_S3C (i.e. it shares the "Series 3c" identity), and in compatibility mode its version_id is WS_TYPE_S3C | WS_VERSION_4 [wserv 7288-7291]. The manuals do not name a "Workabout MX" variant; MX-specific behaviour (notably the barcode scan key, below) is not documented and is marked as reverse-engineered where it appears.


1. The Window Server model

The window server is a system process that provides shared access to the screen and the keyboard (and a pointing device, where one exists) [plib 1295-1296]. Application processes talk to it as clients through the WLIB library — a thin C shell over ROM-based code reached by software interrupts [plib 1338-1339].

1.1 Clients, foreground and background

  • Of all the clients, exactly one is the foreground client; all others are background clients. The foreground client is the one that receives keyboard input [wserv 1906-1910].
  • On the small-screen machines (HC, S3, S3a, Workabout) the windows of background clients are not visible at all; only the foreground client's windows are shown, in front [wserv 1918-1921].
  • On the MC (large screen) windows of several clients can be visible at once, and a client can attach to another [wserv 1330-1333].
  • The server keeps clients in a front-to-back task order: position 0 is the foreground; position 1 is the frontmost background task, and so on [wserv 1968-1971].
  • A client can move itself or another client between foreground and background with wClientPosition [wserv 1983-1984].

1.2 The shared screen and keyboard

Drawing goes into a hierarchical system of overlapping windows; all drawing is clipped to visible areas, and the server issues redraw events telling a client which areas need repainting [plib 1318-1323]. Keyboard input is routed to the foreground client, except for keys the server processes itself (task switch, pause) or keys a client has captured with wCaptureKey so that it receives them whether foreground or not [wserv 1911-1915, 6283-6293].

1.3 Connecting

A process must connect before using the server, via wConnect (or a wrapper such as wStartup) [wserv 1371-1372].

VOID wConnect(WSERV_SPEC *pwserv_spec, VOID *pnws_handle, UINT flags);

[wserv 7182-7183]

  • flags combines: W_CONNECT_AT_BACK (connect as background; default is foreground), W_CONNECT_USER_FLAG, W_CONNECT_SYSTEM_MODAL, W_CONNECT_PRIORITY (enable the server's process-priority handling), W_CONNECT_DISABLE_LEAVES (return negative error numbers instead of calling p_leave) [wserv 7187-7203].
  • pnws_handle is the handle the server puts into events not directed at a window (e.g. key events) [wserv 7206-7207].
  • pwserv_spec points to a WSERV_SPEC the client must keep for the life of the connection. wConnect fills its CONNECT_INFO conn sub-struct with useful data — screen size (pixels), pixel geometry, set_is_dark, version_id (machine type + server version), and the default system_font_handle [wserv 7210-7251].

Connection tests / helpers. After connecting, the reserved static wClientData is non-zero (zero if not connected) [wserv 1882-1894]. The WSERV_SPEC address is also recorded in the reserved static wserv_channel, which general-purpose code can read for screen size etc. [wserv 7294-7303].

Two startup paths (see also §5 on the console):

  • PLIB startup module: call wStartup, which connects, creates and initialises a backed-up window covering the whole screen, and creates a permanent graphics context on it — ready to draw [wserv 1496-1519]. The created window's ID is in the global wMainWid [wserv 1609].
  • CLIB startup module: the startup automatically opens the console device con:, which on the HC/S3/S3a/Workabout is itself a window-server connection — so the process is already a client. Connecting again with wConnect/wStartup panics the process (panic 100) [wserv 1387-1396, 1757]. The console channel is in the static winHandle [wserv 1399-1403].

1.4 Windows (briefly)

  • Create with wCreateWindow (returns a window ID); the window is dormant and invisible until initialised with wInitialiseWindowTree, which activates the window and all descendants [wserv 3141-3167].
  • Destroy a tree with wCloseWindowTree; destroying a window more than once is not treated as an error [wserv 3185-3202].
  • Windows can be backed-up by bitmaps: drawing is mirrored to a backup bitmap and the server redraws automatically, so the client largely avoids redraw handling [wserv 1270-1273, plib 1334-1335].
  • Redraw/mouse events are directed at a window by the handle the client supplied at wCreateWindow (commonly the address of a client structure) [wserv 1944-1946].

2. The event system

For each client the server maintains a queue of events reporting user input and other state changes: key presses, foreground/background changes, redraw events, and (on pointer machines) mouse events [wserv 1926-1937]. Key and mouse events are time-stamped in system ticks (1 tick = 1/32 s) for e.g. double-click detection [wserv 1939-1941, 14331-14333].

2.1 Fetching events

VOID wGetEventWait  (WS_EV *event);              /* block until an event   */
VOID wGetEvent      (WS_EV *event);              /* async, no wait         */
VOID wGetEventSpecial(WS_EV *event, UINT flags); /* async + event filter   */
VOID wGetEventUpdate (UINT flags);               /* change the filter      */

[wserv 14302-14432]

  • wGetEventWait returns only when an event is available; on an empty queue it waits indefinitely [wserv 1949-1951].
  • wGetEvent is the asynchronous version, for clients that also service non-server sources (serial input, timers). It sets event->type = E_FILE_PENDING immediately; when an event arrives the server fills event and signals the client's I/O semaphore. Only one may be outstanding — a second pending wGetEvent panics the process [wserv 14363-14378].
  • wGetEventSpecial (window server version 4 only) is wGetEvent plus a flags filter selecting which events to deliver. Only one call to wGetEvent/wGetEventSpecial may be outstanding at a time [wserv 14381-14393]. Filter flags (OR-able) [wserv 14396-14416]:
    • WE_KEY — key and task-key events
    • WE_REDRAWWM_REDRAW
    • WE_STATUSWM_FOREGROUND, WM_BACKGROUND, WM_ON
    • WE_MOUSE — mouse and rubber-band events
    • WE_OTHERS — everything else
    • WE_NORMAL — all of the above (wGetEventSpecial(..., WE_NORMAL) == wGetEvent)
    • WE_ESC — only meaningful when WE_KEY is not set: on ESC the keyboard buffer is discarded and a WM_ESCAPE event is delivered.
  • wGetEventUpdate (v4) replaces the filter of an outstanding wGetEvent/wGetEventSpecial; a no-op if none is outstanding [wserv 14419-14432].

Async discipline. Fully process one server event before requesting the next. Because the server runs at higher priority than clients, an async request can complete while you are handling some other source. Never destroy a window directly in response to a non-server event — instead call wCancelGetEvent, which delivers a WM_CANCELLED at the highest priority (overtaking all queued but not-yet-delivered events), and do the destruction when that arrives [wserv 3227-3252].

2.2 The event structure

typedef struct {
    WORD  type;             /* positive event type, WM_xxxx        */
    UWORD handle;           /* target window, or the wConnect handle */
    UWORD time;             /* low word of tick count (key/mouse)  */
    WS_EVENT_UNION p;       /* type-dependent payload              */
} WS_EV;

typedef union {
    UWORD       uword;
    UBYTE      *dpoint;
    P_RECT      rect;       /* WM_REDRAW rectangle                 */
    WMSG_KEY    key;        /* WM_KEY                              */
    WMSG_MOUSE  mouse;      /* WM_MOUSE / rubber band              */
    WMSG_RUBBER rubber;
    WMSG_CAPS   caps;       /* WM_KEYBOARD_STATE_CHANGE            */
} WS_EVENT_UNION;

[wserv 14311-14350]

  • handle: for window-directed events (WM_REDRAW, WM_MOUSE) it is the handle given to wCreateWindow; for non-window events (WM_KEY, WM_FOREGROUND) it is the handle given to wConnect [wserv 14326-14329].

The key payload:

typedef struct {
    UWORD keycode;   /* code of the key pressed        */
    UBYTE modifiers; /* shift/ctrl/psion/caps/num-lock */
    UBYTE count;     /* auto-repeat accumulation       */
} WMSG_KEY;

[wserv 14456-14465]

  • modifiers bit flags: W_SHIFT_MODIFIER (0x02), W_CTRL_MODIFIER (0x04), W_PSION_MODIFIER (0x08), W_CAPS_MODIFIER (0x10), W_NUM_LOCK_MODIFIER (0x20, MC only) [wserv 14481-14499].
  • count is 1 for a single press; it exceeds 1 only when the client cannot keep up with auto-repeat. The manual's advice is to ignore the repeat count [wserv 14466-14476, 6263-6280].
  • keycode: printable SIBO characters (code page 850-like) fall in 0x200xFF excluding 0x7F. The PSION shift typically adds 0x200 (W_SPECIAL_KEY). "Special" keys carry codes < 0x20, 0x7F, or > 0xFF [wserv 14501-14541]. Named special codes include W_KEY_TAB (0x09), W_KEY_DELETE_LEFT (0x08), W_KEY_DELETE_RIGHT (0x7F), W_KEY_RETURN (0x0D), W_KEY_ESCAPE (0x1B), W_KEY_UP/DOWN/RIGHT/LEFT (0x1000x103), W_KEY_PAGE_UP/DOWN (0x104/0x105), W_KEY_HOME/END (0x106/0x107), W_KEY_TASK (0x108), W_KEY_MENU (0x122), W_KEY_HELP (0x123), W_KEY_ON (0x2002), W_KEY_OFF (0x2003), etc. [wserv 14547-14852].

2.3 Event types

Common event types (WS_EV event; assumed) [wserv 14441-15048]:

Event Meaning Payload
WM_KEY A key was pressed. The full description is in event.p.key (keycode, modifiers, count). Delivered to the foreground client (or a capturer). [wserv 14449-14476, 6249-6250] p.key
WM_REDRAW Sent when the client's queue is empty and one or more windows has an update region. event.p.rect is a rectangular block of pixels needing redraw. Lowest priority except for WM_USER_MSG. [wserv 14855-14865] p.rect
WM_BACKGROUND The foreground client has just gone to background. Usually nothing to do, but suspend real-time/animated activity until foreground returns. Only type set. [wserv 14868-14878]
WM_FOREGROUND A background client has just become foreground. Only type set. [wserv 14881-14887]
WM_CANCELLED Sent in response to wCancelGetEvent; delivered at highest priority. Only type set. [wserv 14890-14897, 3250-3252]
WM_USER_MSG Sent in response to wUserMsg. Lowest priority of all — useful as an "the server has nothing more to send" marker. Only type set. [wserv 14909-14916]
WM_ON Machine switched on (server v3.5+). Delivered to the foreground client if it called wInformOn; in v4, to any client (fore or back) that called wInformOnAll(TRUE). Prompts a display refresh. Only type set. [wserv 14919-14937]
WM_COMMAND Another client sent a command via wSendCommand (server v3.5). Prompts the receiver to call wGetCommand for the data (up to 127 bytes). Only type set. [wserv 14940-14947, 1128-1129]
WM_TASK_KEY Sent to the application-key handler (the shell) when an application key is pressed with no process of that application present, or a PSION-shifted application key is pressed. S3/S3a/Workabout only. The app-key index (015) is in event.p.key.keycode. [wserv 14963-14975] p.key.keycode
WM_ESCAPE (v4) Delivered when ESC is pressed and the client selected events with wGetEventSpecial including WE_ESC/WM_ESCAPE but excluding key events; the keyboard buffer is discarded. [wserv 14999-15015]
WM_TASK_UPDATE Sent to the shell (when foreground) whenever any process terminates. S3/S3a/Workabout and HC v3.5+. Only type set. [wserv 14950-14960]
WM_DATE_CHANGED (v4) Date changed (reset, or past midnight). Sent to a foreground S3a/Workabout app not in S3 compatibility mode; background apps get it on next foreground; delivered at next power-on if off. [wserv 14978-14993]
WM_KEYBOARD_STATE_CHANGE Sent only to the shell when numlock/capslock changes; new state in event.p.caps.modifiers. (MC generates it for the caps-lock key.) [wserv 15065-15069, 14734-14735] p.caps

Mouse / pointer events (only on machines with a pointing device, e.g. the MC) [wserv 15076-15187]:

Event Meaning
WM_MOUSE State change on the digitiser. event.p.mouse is a WMSG_MOUSE { UBYTE event; UBYTE state; P_POINT pos; }. event.p.mouse.event is WM_MOUSE_MOVE (filtered out by default), WM_MOUSE_PRESS, or WM_MOUSE_RELEASE; state carries W_MOUSE_DOWN, W_MOUSE_OUTSIDE, and the shift modifiers. [wserv 15083-15129]
WM_RUBBER_BAND_INIT Special WM_MOUSE variant sent when a press occurs in a window flagged W_WIN_RUBBER_BAND_CAPTURE. [wserv 15146-15154]
WM_RUBBER Sent on completion of a rubber-band interaction. [wserv 15160-15166]
WM_ACTIVE Sent to a window that set W_WIN_INACTIVE when a WM_MOUSE_PRESS lands on it or a descendant (in place of the WM_MOUSE); the client typically re-activates the tree by clearing W_WIN_INACTIVE. Only type set. [wserv 15169-15186]

Large-screen (MC) events also include WM_DEICONISE, WM_ATTACHED, WM_DETACHED for the multi-task attach/iconise model [wserv 15017-15062].

2.4 Event priority

Event delivery is prioritised, not strictly FIFO:

  • WM_CANCELLED is delivered at the highest priority, overtaking anything queued but not yet delivered [wserv 3250-3252].
  • WM_REDRAW is lower than user input and foreground/background events; the only type lower than WM_REDRAW is WM_USER_MSG [wserv 14865, 3126-3127].
  • WM_USER_MSG has the lowest priority of all [wserv 14912-14913].
  • Between windows there is a further two-level redraw priority: windows are low by default; W_WIN_PRIORITY (on wCreateWindow/wSetWindow) promotes a window's redraws ahead of the rest [wserv 3130-3131].

2.5 The barcode scan key — reverse-engineered (Workabout MX)

Not in the manuals. The Window Server Reference documents no barcode / scanner / laser trigger key, and defines no keycode near 368. On barcode- equipped Workabout / Workabout MX hardware the scan (trigger) key is observed to arrive as an ordinary WM_KEY event with keycode 368 (0x170). This is reverse-engineered field knowledge, not documented behaviour — treat the exact value and delivery path as version/hardware dependent and confirm on the target unit. Because it surfaces as a normal key event, it can be handled in the ordinary WM_KEY path (and, like other keys, potentially captured with wCaptureKey).


3. Drawing and text output primitives

All drawing targets a drawable selected by the current graphics context (GC) [wserv 5504, 5526].

3.1 Graphics contexts

  • Create a permanent GC on a window/bitmap with gCreateGC / gCreateGC0 (the latter with default values) [wserv 304-305, 1438]. Select the current GC with gSetGC / gSetGC0 [wserv 311-312, 2993].
  • Temporary GCs are used during redraws (see below) and are freed automatically by wEndRedraw [wserv 3037-3050].
  • wStartup conveniently makes a permanent GC on its full-screen window [wserv 1502-1505].

3.2 Graphics primitives

  • Lines / shapes / borders / fills: gDrawLine, gBorderRect, gBorder, gBorder2Rect/gBorder2 (shadowed, v4), gFillPattern, gInvObloid [wserv 315-326, 1296, 919-957].
  • Bitmaps: gCopyBit (copy a bitmap to a window) and the bitmap-file family (gInitBit, gGetBit, gLoadBit, gPeekBit, gSaveBit) [wserv 354, 829-1021].
  • The server also draws buttons (wDrawButton, wDrawButton2), sprites (wCreateSprite/wSetSprite) and scaled objects (gDrawObject) [wserv 926-969, 1168, 1203].

3.3 Text output

Text is drawn through the current GC (font ID, style, transfer mode) [wserv 5526-5536]:

  • gPrintText — draw text from a pixel position [wserv 5508, 1439]
  • gPrintClipText — clipped to a given width [wserv 5510]
  • gPrintBoxText — left/right/centred in a box; commonly used for flicker-free redraw [wserv 5512-5513, 2961]
  • gXPrintText — with embellishment [wserv 5515]
  • gShadowText — shadowed (v4) [wserv 5517]

Font styles combine G_STY_NORMAL, G_STY_BOLD, G_STY_UNDERLINE, G_STY_INVERSE, G_STY_DOUBLE, G_STY_MONO, G_STY_ITALIC [wserv 5539-5557]. Layout helpers: gTextWidth, gTextCount, wGetWidthTable [wserv 5492-5497].

3.4 Redraw handling

On a WM_REDRAW, the normal response is to validate the update region and draw it [wserv 10248-10256]. Bracket the drawing with a wBeginRedraw variant and wEndRedraw; wBeginRedraw both signals "this is a redraw" and validates the given rectangle [wserv 3005-3007]. There are six wBeginRedraw* variants covering partial vs whole window and independent/temporary GC combinations [wserv 3020-3050]. Related: validate without redrawing (wValidateRect, wValidateWin) and force redraws by invalidating (wInvalidateRect, wInvalidateWin) [wserv 270-274, 2901]. Backed-up windows largely avoid this cycle because the server repaints from the backup bitmap [wserv 1270-1273].

Minimal MC (non-backed-up) redraw loop [wserv 1589-1608]:

wStartup();
do {
    wGetEventWait(&event);
    if (event.type == WM_REDRAW) {
        wBeginRedrawWin(wMainWid);
        gPrintText(10, 20, "Hello world!", 12);
        wEndRedraw();
    }
} while (event.type != WM_KEY);

4. Error handling (server calls)

By default a failing server function calls p_leave with the negative error number; wDisableLeaves(TRUE) (or W_CONNECT_DISABLE_LEAVES at connect time) makes it return the error instead [wserv 1629-1641]. Many drawing calls are blind (queued client-side, no acknowledgement); on failure the server discards further blind ops until a non-blind call reports the error. Force the issue with wCheckPoint (flush + signal) or wFlush (flush only); after an error, wCleanUp releases dangling resources [wserv 1653-1703]. Client-side buffering means a panic can lag the offending call — insert wFlush/ wCheckPoint when hunting one down [wserv 1791-1793].


5. The console layer — CON: and the PLIB console functions

The console is the lightweight text-UI path. PLIB contains only primitive console functions — typed-line input (with simple backspace editing) and mono-spaced line output — while the con: device driver adds row/column positioning and mono-spaced character printing [plib 1299-1301]. They are explicitly "for quick-and-dirty applications... not suitable for constructing quality user interfaces" [plib 8580-8581].

5.1 Relationship to the window server

Both the PLIB console functions and con: ultimately call the window server for input and drawing — but expose only a fraction of its capability [plib 1310-1315]. How con: is implemented differs by machine [wserv 1319-1323]:

  • HC / S3 / S3a / Workabout: con: is implemented by connecting directly to the window server — opening con: makes the process a window- server client. Opening it also creates and initialises a backed-up console window (no redraw handling needed) [wserv 1319-1320, 1387-1407]. The console window's ID can be fetched with the P_FINQ I/O function (CONSOLE_INFO.window_handle), letting a CLIB program create its own GC on that window and draw with WLIB [wserv 1410-1453].
  • MC: con: is implemented by a separate SYS$CONS display process that keeps a character map and redraws its own window (with title/menu bars). Here it is SYS$CONS, not the application, that is the window-server client; the application cannot use the console window, and P_FINQ is not supported. On the MC the recommendation is the PLIB startup module + wStartup [wserv 1305-1322, 1539-1615].

The two startup modules therefore differ: the CLIB startup auto-opens con: (backing printf, gets, cprintf, cgets) [wserv 1387-1389]; the PLIB startup does not, and you call wStartup yourself [wserv 1496-1497]. Auto-opening can be suppressed in CLIB by defining p_xwind [wserv 1457-1490].

5.2 The PLIB console functions

Output (open con: automatically on first use) [plib 8584-8595, 8700-8737]:

VOID p_putch(UINT c);            /* write one character                    */
VOID p_puts (TEXT *str);         /* write string + newline                 */
VOID p_printf(TEXT *fstr, ...);  /* formatted line + newline               */
VOID p_print (TEXT *fstr, ...);  /* formatted, no trailing newline         */
  • p_printf uses an internal buffer of P_MAXSYSIO (258) bytes; output is limited to P_MAXSYSIO - 2 (256) bytes per call. Format is that of p_atob [plib 8720-8726].
  • p_print moves no line; embed \r (start of line) and \n (down a line) yourself [plib 8732-8737].

Input [plib 8597-8599, 8740-8770]:

INT p_getch(VOID);                       /* wait for a key, return its code */
INT p_gets (TEXT *str);                  /* read a line (backspace edit)    */
INT p_getl (TEXT *pmt, TEXT *str, INT len); /* prompt, then read a line     */
  • p_getch waits for a key and returns its character code (no echo) [plib 8740-8743].
  • p_gets reads up to P_MAXSYSIO-1 characters, Enter-terminated, and NUL- terminates at str (needs P_MAXSYSIO bytes there); returns the length [plib 8746-8756].
  • p_getl writes the prompt pmt, then reads up to len characters (Enter-terminated, needs len+1 bytes); returns the length [plib 8759-8770].

Canonical smallest program [plib 1020-1021]:

p_printf("Hello world");
p_getch();

5.3 Console configuration statics

Set these before the first console call [plib 8612-8694]:

  • winHandle (GLREF_D VOID *) holds the open console channel; open a file or TTY: into it first to redirect p_putch / p_print / p_printf. Do not use p_getch / p_gets / p_getl when redirected [plib 8615-8628].
  • _DefScreenRect (P_RECT, top-left (0,0), bottom-right = columns × rows) changes the console window size [plib 8629-8665].
  • _DefScreenMode (INT) selects the mode: 0 native (default, currently single-pixel non-compatibility on all SIBO machines), 1 compatibility, 2 non-compatibility with grey, 3 compatibility with grey. Irrelevant values are ignored [plib 8668-8694].

6. Higher UI libraries (pointer only)

For full applications — menus, dialogs, forms — SIBO provides higher-level UI libraries layered on the window server (commonly referred to as HWIM, FORM and OLIB). These are separate libraries and are not documented in the Window Server Reference or PLIB Reference covered here; the two manuals mention menus and dialog boxes only as windows the server draws/clips (e.g. [wserv 2395, 10646-10647]), not as an API. Use those libraries' own references for real application UIs; the material above is the foundation they sit on. (Library names are noted as a pointer only and are not sourced from these two manuals.)


Source key

  • [wserv N]Window Server Reference v2.30, line N of the supplied text.
  • [plib N]PLIB Reference (console functions), line N of the supplied text.
  • Items marked reverse-engineered / uncertain are not in either manual (notably the code-368 barcode scan key and the higher UI library names).