joeylib2/docs/ApiInput.adoc
2026-09-29 21:25:37 -05:00

262 lines
7.2 KiB
Text

== Input
Declared in `<joey/input.h>`.
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. On DVX's full screen it is the pointer's
place on the stage (DVX draws no pointer there).
=== 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.
====