joeylib2/docs/ApiInput.adoc

326 lines
11 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 {
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 where TOS keeps its keyboard's Alt
layer in its tables (TOS 4.00 and later, and EmuTOS: a German keyboard's
@ [ ] { } \), and otherwise nothing (<<portsDifferInput>>). 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
(<<portsDifferInput>>).
=== 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.
====