== 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 { KEY_NONE = 0, KEY_A .. KEY_Z, KEY_0 .. KEY_9, KEY_SPACE, KEY_ESCAPE, KEY_RETURN, KEY_TAB, KEY_BACKSPACE, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_LSHIFT, KEY_RSHIFT, KEY_LCTRL, KEY_LALT, KEY_F1 .. KEY_F10, KEY_COUNT } jlKeyE; typedef enum { MOUSE_BUTTON_NONE = 0, MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_COUNT } jlMouseButtonE; typedef enum { JOYSTICK_0 = 0, JOYSTICK_1, JOYSTICK_COUNT } jlJoystickE; typedef enum { JOY_BUTTON_0 = 0, JOY_BUTTON_1, JOY_BUTTON_COUNT } jlJoyButtonE; ---- Constants: `JOYSTICK_AXIS_MAX` (127) and `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. === jlInputPoll [source,c] ---- void jlInputPoll(void); ---- Refreshes all input state and refills the typed-character queue. Call once per frame, typically just before drawing. *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 IIgs, Amiga, ST and X68000, and DVX's own on DVX; the DOS port translates with a fixed US layout. 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. Auto-repeat may deliver repeats on ports whose keyboard services repeat; 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 any key, polling internally so per-port input machinery -- including interrupt-driven audio samplers -- keeps running during the wait. *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. === 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 Amiga, ST, IIgs, X68000 and DVX the OS mouse is always wired. 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. === 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. === 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. === jlJoystickConnected [source,c] ---- bool jlJoystickConnected(jlJoystickE js); ---- [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`js` |`jlJoystickE` |Which stick, `JOYSTICK_0` or `JOYSTICK_1`. |=== *Returns:* `true` if the platform reports a stick on that port. === 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 snap to the extremes; analog sticks return the raw centred value. 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. === jlJoystickReset [source,c] ---- void jlJoystickReset(jlJoystickE js, uint8_t deadZone); ---- Re-enables joystick polling and recalibrates the resting position. This matters on the IIgs. That port *auto-disables* joystick polling after a short window with no stick detected, because polling costs about 3 ms a frame there -- and it does not re-probe on its own. If a user plugs a stick in while your program runs, you must call this to resume polling. [cols="1,1,4", options="header"] |=== |Parameter |Type |Description |`js` |`jlJoystickE` |Which stick. |`deadZone` |`uint8_t` |Raw readings within this many units of centre clamp to 0. Pass 0 to disable the dead zone. |=== *Returns:* nothing. [IMPORTANT] ==== The next poll after this call captures the stick's *current* raw position as the new centre, so the user must be holding the stick centred when you call it. On Amiga, ST and DOS the sticks are genuinely digital and already report -1, 0 or +1, so recalibration is a no-op and `deadZone` is ignored. DVX's sticks are analog but calibrated, dead zone included, in DVX's own Control Panel, so the call is a no-op there too. The call still clears any auto-disconnect state, so it is safe and correct to make it unconditionally. ====