== Input Declared in ``. Call `jlInputPoll()` exactly once per frame to refresh keyboard, mouse, and joystick state. Everything else in this section reads the state that poll captured. Two parallel keyboard interfaces share that one poll. The *predicates* answer "is the fire key held?", which is the right shape for games. The *character queue* answers "which character did the user type?", with shift, caps lock, and the machine's keyboard layout already applied, which is the right shape for text entry. They coexist freely. Edge predicates are one-shot: they return `true` only in the frame the transition happened. === Enumerations [source,c] ---- typedef enum { JL_KEY_NONE = 0, JL_KEY_A .. JL_KEY_Z, JL_KEY_0 .. JL_KEY_9, JL_KEY_SPACE, JL_KEY_ESCAPE, JL_KEY_RETURN, JL_KEY_TAB, JL_KEY_BACKSPACE, JL_KEY_UP, JL_KEY_DOWN, JL_KEY_LEFT, JL_KEY_RIGHT, JL_KEY_LSHIFT, JL_KEY_RSHIFT, JL_KEY_LCTRL, JL_KEY_LALT, JL_KEY_F1 .. JL_KEY_F10, JL_KEY_COUNT } jlKeyE; typedef enum { JL_MOUSE_BUTTON_NONE = 0, JL_MOUSE_BUTTON_LEFT, JL_MOUSE_BUTTON_RIGHT, JL_MOUSE_BUTTON_MIDDLE, JL_MOUSE_BUTTON_COUNT } jlMouseButtonE; typedef enum { JL_JOYSTICK_0 = 0, JL_JOYSTICK_1, JL_JOYSTICK_COUNT } jlJoystickE; typedef enum { JL_JOY_BUTTON_0 = 0, JL_JOY_BUTTON_1, JL_JOY_BUTTON_COUNT } jlJoyButtonE; ---- Constants: `JL_JOYSTICK_AXIS_MAX` (127) and `JL_JOYSTICK_AXIS_MIN` (-127); `JL_CHAR_QUEUE_SIZE` (32). Control codes delivered through the character queue: `JL_CHAR_BACKSPACE` (0x08), `JL_CHAR_TAB` (0x09), `JL_CHAR_RETURN` (0x0D), `JL_CHAR_ESCAPE` (0x1B), and `JL_CHAR_DELETE` (0x7F). That last one is *forward* delete, the key marked Del -- a text editor needs both it and backspace. The IIgs never delivers it: its Delete key is the backspace key, and types `JL_CHAR_BACKSPACE`. === jlInputPoll [source,c] ---- void jlInputPoll(void); ---- Refreshes all input state and refills the typed-character queue. Call once per frame, typically just before drawing. A key pressed and let go between two polls still reads down for one poll, so `jlKeyPressed` sees a quick tap (except on DVX, which reads its keyboard only as it is at the poll). *Parameters:* none. *Returns:* nothing. [WARNING] ==== Exactly once per frame. Polling twice makes edge predicates miss transitions; not polling means nothing ever changes. ==== === jlInputGetChar [source,c] ---- int jlInputGetChar(void); ---- Pops the next typed character. Shift, caps lock, and the keyboard layout have already been applied by the backend, so punctuation and shifted symbols arrive correctly. The layout is the machine's own on the IIgs, Amiga, ST, and X68000. It is DVX's own on DVX, and the operating system's on SDL3, the web, Win32, Win16, Haiku, and BeOS. DOS, and the PSP's on-screen keyboard, use a fixed US layout. On the ST a key held with Alt types nothing, so the characters a national TOS puts on Alt cannot be typed. Arrow keys are not characters and stay on the predicate API. *Parameters:* none. *Returns:* a 7-bit code -- printable ASCII 0x20..0x7E, or one of the `JL_CHAR_*` control codes -- or `-1` if the queue is empty. [NOTE] ==== The queue holds `JL_CHAR_QUEUE_SIZE - 1` characters, so 31. When full, further characters are dropped until you pop some. A chord types nothing on any port: Ctrl+M is not Return and Ctrl+1 is not 1, and Alt chords type nothing where Alt is a command key (DOS, DVX, Windows, the Be ports' Command key, the PSP's on-screen keyboard); the Amiga's Alt and Windows' AltGr type their layout's characters, and so does the ST's Alt, the characters its national keyboard's Alt layer gives (a German keyboard's @ [ ] { } \; <>). A held key repeats its character on every port (the ST at the rate TOS is set to); repeat policy is yours to manage. 7-bit ASCII only -- no Unicode, no input methods. The return type is plain `int`, not a stdint type, deliberately: this is the `getchar()`-style "byte or -1" contract. ==== === jlWaitForAnyKey [source,c] ---- void jlWaitForAnyKey(void); ---- Blocks until the user presses a key -- one JoeyLib names (with a `jlKeyE`), or one that types a character, punctuation for instance -- polling internally so per-port input machinery, including interrupt-driven audio samplers, keeps running during the wait. The mouse and joystick buttons do not end it, and a key already held when the wait starts must be let go and pressed again (its auto-repeat does not count). *Parameters:* none. *Returns:* nothing. The character queue is emptied on return, so the dismissing keystroke is consumed rather than delivered as text. === jlKeyDown / jlKeyPressed / jlKeyReleased [source,c] ---- bool jlKeyDown(jlKeyE key); bool jlKeyPressed(jlKeyE key); bool jlKeyReleased(jlKeyE key); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`key` |`jlKeyE` |Key to test. |=== *Returns:* `jlKeyDown` is `true` while the key is held. `jlKeyPressed` is `true` only in the frame the key went down; `jlKeyReleased` only in the frame it came up. A named key that two physical keys give is down while either is held, and comes up with the last of them: `JL_KEY_RETURN` is Return and the keypad's Enter; `JL_KEY_UP`, `JL_KEY_DOWN`, `JL_KEY_LEFT`, and `JL_KEY_RIGHT` are the arrow keys and the keypad's 8, 2, 4, and 6; and `JL_KEY_LCTRL` and `JL_KEY_LALT` are the left and right Ctrl and Alt, where the keyboard has both (on the X68000, OPT.1 and OPT.2 are Alt). The keypad's keys still type what the machine's keypad types (`jlInputGetChar`). The IIgs knows keys by their characters, so its keypad reads as the digits (<>). === jlMousePresent [source,c] ---- bool jlMousePresent(void); ---- *Parameters:* none. *Returns:* `true` if the platform confirmed a mouse at init. On DOS that means the INT 33h driver reset successfully; on the Amiga, that JoeyLib's input window opened; on Win32 and Win16, that Windows reports a mouse; on SDL3 and the web, that SDL reports one. The IIgs, ST, X68000, DVX, Haiku, and BeOS always report one, and the PSP never does. === jlMouseX / jlMouseY [source,c] ---- int16_t jlMouseX(void); int16_t jlMouseY(void); ---- *Parameters:* none. *Returns:* pointer position in surface coordinates, 0..319 and 0..199. Until the platform reports the pointer, it is the middle of the stage, (160, 100), on every port. On DVX the position is the pointer's over the program's window, divided by the window's scale; it keeps its last value while the pointer is elsewhere. On DVX's full screen it is the pointer's place on the stage (DVX draws no pointer there). On the hosted ports it is the pointer's place on the stage however the window scales it, held to the stage's edge while the pointer is beside it; the OS pointer is hidden over the window. On DOS the mouse driver keeps it in range. === jlMouseDown / jlMousePressed / jlMouseReleased [source,c] ---- bool jlMouseDown(jlMouseButtonE button); bool jlMousePressed(jlMouseButtonE button); bool jlMouseReleased(jlMouseButtonE button); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`button` |`jlMouseButtonE` |Which button. |=== *Returns:* held state, rising edge, and falling edge respectively. The IIgs mouse has one button, `JL_MOUSE_BUTTON_LEFT`. The ST and X68000 have left and right. The other ports report left, right, and middle, as far as the mouse has them. The PSP has no mouse. === jlJoystickConnected [source,c] ---- bool jlJoystickConnected(jlJoystickE js); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`js` |`jlJoystickE` |Which stick, `JL_JOYSTICK_0` or `JL_JOYSTICK_1`. |=== *Returns:* `true` if the platform reports a stick on that port. On the ST and X68000 both ports always report one: an empty port cannot be told from a stick at rest. The Amiga (`lowlevel.library`) has the same blind spot the other way: it reports a stick only once it has moved, and never a mouse. The IIgs has one stick, `JL_JOYSTICK_0`, and stops looking for it after about a second with none seen, until `jlJoystickReset`. On DOS a stick is connected while the game port times both its axes. SDL3, the web, and the PSP see SDL's gamepads and joysticks both, a joystick SDL has no gamepad mapping for read by its first two axes, its first hat, and its first two buttons; Win32, Win16, Haiku, and BeOS see the system's joysticks, and Haiku and BeOS notice a new one within a second. === jlJoystickX / jlJoystickY [source,c] ---- int8_t jlJoystickX(jlJoystickE js); int8_t jlJoystickY(jlJoystickE js); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`js` |`jlJoystickE` |Which stick. |=== *Returns:* axis value, -127 to +127. Digital sticks (the Amiga, ST, and X68000) snap to the extremes; analog sticks (the IIgs, DOS, DVX, and the hosted ports) return their offset from the centre, scaled to that range, with the dead zone applied. On the IIgs and DOS the axes read 0 until the stick's centre has been read (<<_jljoystickreset>>). Test against a threshold rather than for exact values, so the same code works with both. === jlJoyDown / jlJoyPressed / jlJoyReleased [source,c] ---- bool jlJoyDown(jlJoystickE js, jlJoyButtonE button); bool jlJoyPressed(jlJoystickE js, jlJoyButtonE button); bool jlJoyReleased(jlJoystickE js, jlJoyButtonE button); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`js` |`jlJoystickE` |Which stick. |`button` |`jlJoyButtonE` |Which button. |=== *Returns:* held state, rising edge, and falling edge respectively. Every stick has two buttons. On the ST and STe's own joystick ports, and on the Amiga until a stick's second button is first pressed, a stick is taken to have one: a quick press of it is `JL_JOY_BUTTON_0`, down for the one poll after it is let go, and a press held for half a second is `JL_JOY_BUTTON_1`, down from then until it is let go. So act on button 0 with `jlJoyPressed`, and do not count on it staying down while the button is held. Elsewhere the two buttons are the stick's own, and a one-button stick never reports `JL_JOY_BUTTON_1`. === jlJoystickReset [source,c] ---- void jlJoystickReset(jlJoystickE js, uint8_t deadZone); ---- Sets the stick's dead zone and, where the port can, re-centres it and looks for it again: * *IIgs*: polling starts again if it had stopped for want of a stick, and the stick is re-centred at its next reading. * *DOS*: the game port is probed again, and the stick is re-centred at its next reading. * *Amiga*: the port goes back to `lowlevel.library`'s own detection. * *Elsewhere* only the dead zone changes. On the IIgs polling for a stick that is not there stops. If a user may plug a stick in while your program runs, call this, with the stick centred, to pick it up. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`js` |`jlJoystickE` |Which stick. |`deadZone` |`uint8_t` |Axis values within this many units of 0 (on the -127..127 scale) read 0, on the ports with analog sticks: the IIgs, DOS, DVX, and the hosted ports. Each stick starts with 8, so a stick left alone reads 0 though its pots jitter. Pass 0 to disable the dead zone. |=== *Returns:* nothing. [IMPORTANT] ==== On the IIgs and DOS the next reading after this call is taken as the stick's centre, so the user must be holding the stick centred when you call it. The call is safe to make unconditionally. ====