24 KiB
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 itsversion_idisWS_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]
flagscombines: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 callingp_leave)[wserv 7187-7203].pnws_handleis the handle the server puts into events not directed at a window (e.g. key events)[wserv 7206-7207].pwserv_specpoints to aWSERV_SPECthe client must keep for the life of the connection.wConnectfills itsCONNECT_INFO connsub-struct with useful data — screen size (pixels), pixel geometry,set_is_dark,version_id(machine type + server version), and the defaultsystem_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 globalwMainWid[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 withwConnect/wStartuppanics the process (panic 100)[wserv 1387-1396, 1757]. The console channel is in the staticwinHandle[wserv 1399-1403].
1.4 Windows (briefly)
- Create with
wCreateWindow(returns a window ID); the window is dormant and invisible until initialised withwInitialiseWindowTree, 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]
wGetEventWaitreturns only when an event is available; on an empty queue it waits indefinitely[wserv 1949-1951].wGetEventis the asynchronous version, for clients that also service non-server sources (serial input, timers). It setsevent->type = E_FILE_PENDINGimmediately; when an event arrives the server fillseventand signals the client's I/O semaphore. Only one may be outstanding — a second pendingwGetEventpanics the process[wserv 14363-14378].wGetEventSpecial(window server version 4 only) iswGetEventplus aflagsfilter selecting which events to deliver. Only one call towGetEvent/wGetEventSpecialmay be outstanding at a time[wserv 14381-14393]. Filter flags (OR-able)[wserv 14396-14416]:WE_KEY— key and task-key eventsWE_REDRAW—WM_REDRAWWE_STATUS—WM_FOREGROUND,WM_BACKGROUND,WM_ONWE_MOUSE— mouse and rubber-band eventsWE_OTHERS— everything elseWE_NORMAL— all of the above (wGetEventSpecial(..., WE_NORMAL)==wGetEvent)WE_ESC— only meaningful whenWE_KEYis not set: on ESC the keyboard buffer is discarded and aWM_ESCAPEevent is delivered.
wGetEventUpdate(v4) replaces the filter of an outstandingwGetEvent/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 aWM_CANCELLEDat 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 towCreateWindow; for non-window events (WM_KEY,WM_FOREGROUND) it is the handle given towConnect[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]
modifiersbit 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].countis 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 in0x20–0xFFexcluding0x7F. The PSION shift typically adds0x200(W_SPECIAL_KEY). "Special" keys carry codes< 0x20,0x7F, or> 0xFF[wserv 14501-14541]. Named special codes includeW_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(0x100–0x103),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 (0–15) 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_CANCELLEDis delivered at the highest priority, overtaking anything queued but not yet delivered[wserv 3250-3252].WM_REDRAWis lower than user input and foreground/background events; the only type lower thanWM_REDRAWisWM_USER_MSG[wserv 14865, 3126-3127].WM_USER_MSGhas 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(onwCreateWindow/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_KEYevent 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 ordinaryWM_KEYpath (and, like other keys, potentially captured withwCaptureKey).
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 withgSetGC/gSetGC0[wserv 311-312, 2993]. - Temporary GCs are used during redraws (see below) and are freed
automatically by
wEndRedraw[wserv 3037-3050]. wStartupconveniently 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 — openingcon: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 theP_FINQI/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 separateSYS$CONSdisplay process that keeps a character map and redraws its own window (with title/menu bars). Here it isSYS$CONS, not the application, that is the window-server client; the application cannot use the console window, andP_FINQis 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_printfuses an internal buffer ofP_MAXSYSIO(258) bytes; output is limited toP_MAXSYSIO - 2(256) bytes per call. Format is that ofp_atob[plib 8720-8726].p_printmoves 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_getchwaits for a key and returns its character code (no echo)[plib 8740-8743].p_getsreads up toP_MAXSYSIO-1characters, Enter-terminated, and NUL- terminates atstr(needsP_MAXSYSIObytes there); returns the length[plib 8746-8756].p_getlwrites the promptpmt, then reads up tolencharacters (Enter-terminated, needslen+1bytes); 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 orTTY:into it first to redirectp_putch/p_print/p_printf. Do not usep_getch/p_gets/p_getlwhen 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:0native (default, currently single-pixel non-compatibility on all SIBO machines),1compatibility,2non-compatibility with grey,3compatibility 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).