201 lines
8.3 KiB
C
201 lines
8.3 KiB
C
// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
|
// See LICENSE.md for license information.
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
// Keyboard, mouse, and joystick input polling.
|
|
//
|
|
// Call jlInputPoll() once per frame (typically right before
|
|
// drawing) to refresh keyboard, mouse, and joystick state. After
|
|
// polling, the jlKey* predicates return the current state of every
|
|
// key:
|
|
//
|
|
// jlKeyDown(k) -- is key k held down right now
|
|
// jlKeyPressed(k) -- rising edge since the previous poll
|
|
// jlKeyReleased(k) -- falling edge since the previous poll
|
|
//
|
|
// Typed-character input (text entry) is a separate path:
|
|
//
|
|
// jlInputGetChar() -- pop the next typed character, or -1
|
|
//
|
|
// The key predicates are the right shape for games (is the fire key
|
|
// held?); the character queue is the right shape for text fields
|
|
// (which character did the user type, with shift and the machine's
|
|
// keyboard layout already applied). Both are refreshed by the same
|
|
// jlInputPoll() and coexist freely.
|
|
//
|
|
// The mouse predicates return the pointer state:
|
|
//
|
|
// jlMouseX/Y() -- pointer position in surface
|
|
// coords (0..JL_SURFACE_WIDTH-1,
|
|
// 0..JL_SURFACE_HEIGHT-1)
|
|
// jlMouseDown(b) -- is button b held right now
|
|
// jlMousePressed(b) -- rising edge since last poll
|
|
// jlMouseReleased(b) -- falling edge since last poll
|
|
//
|
|
// The joystick predicates return per-stick state:
|
|
//
|
|
// jlJoystickConnected(js) -- true if the platform reports a
|
|
// stick on this port
|
|
// jlJoystickX/Y(js) -- axis values, signed -127..+127.
|
|
// Digital sticks snap to the
|
|
// extremes; analog sticks return
|
|
// their offset from centre, scaled,
|
|
// dead zone applied.
|
|
// jlJoyDown(js, b) -- is button b held on stick js
|
|
// jlJoyPressed(js, b) -- rising edge since last poll
|
|
// jlJoyReleased(js, b) -- falling edge since last poll
|
|
//
|
|
// Edge predicates are one-shot: they return true only in the
|
|
// frame the transition occurred and false thereafter.
|
|
|
|
#ifndef JOEYLIB_INPUT_H
|
|
#define JOEYLIB_INPUT_H
|
|
|
|
#include "platform.h"
|
|
#include "types.h"
|
|
|
|
typedef enum {
|
|
JL_KEY_NONE = 0,
|
|
|
|
JL_KEY_A, JL_KEY_B, JL_KEY_C, JL_KEY_D, JL_KEY_E, JL_KEY_F, JL_KEY_G, JL_KEY_H, JL_KEY_I,
|
|
JL_KEY_J, JL_KEY_K, JL_KEY_L, JL_KEY_M, JL_KEY_N, JL_KEY_O, JL_KEY_P, JL_KEY_Q, JL_KEY_R,
|
|
JL_KEY_S, JL_KEY_T, JL_KEY_U, JL_KEY_V, JL_KEY_W, JL_KEY_X, JL_KEY_Y, JL_KEY_Z,
|
|
|
|
JL_KEY_0, JL_KEY_1, JL_KEY_2, JL_KEY_3, JL_KEY_4,
|
|
JL_KEY_5, JL_KEY_6, JL_KEY_7, JL_KEY_8, 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_F2, JL_KEY_F3, JL_KEY_F4, JL_KEY_F5,
|
|
JL_KEY_F6, JL_KEY_F7, JL_KEY_F8, JL_KEY_F9, 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;
|
|
|
|
#define JL_JOYSTICK_AXIS_MAX 127
|
|
#define JL_JOYSTICK_AXIS_MIN (-127)
|
|
|
|
// Control characters delivered through jlInputGetChar alongside
|
|
// printable ASCII. Everything else below 0x20, and everything above
|
|
// 0x7F, is filtered out of the queue.
|
|
#define JL_CHAR_BACKSPACE 0x08
|
|
#define JL_CHAR_TAB 0x09
|
|
#define JL_CHAR_RETURN 0x0D
|
|
#define JL_CHAR_ESCAPE 0x1B
|
|
// The FORWARD delete (the key labelled Del/Delete on a PC, ST or Amiga keyboard - not backspace). A text
|
|
// editor needs both: backspace removes the character BEFORE the caret, this one removes the character
|
|
// UNDER it. 0x7F is ASCII DEL. The IIgs never delivers it: its Delete key is backspace.
|
|
#define JL_CHAR_DELETE 0x7F
|
|
|
|
// Typed-character queue capacity (ring buffer; one slot stays empty,
|
|
// so JL_CHAR_QUEUE_SIZE - 1 characters can be pending). Must be a
|
|
// power of two.
|
|
#define JL_CHAR_QUEUE_SIZE 32
|
|
|
|
void jlInputPoll(void);
|
|
|
|
// Pop the next typed character as a 7-bit code, or -1 if the queue
|
|
// is empty. Codes: printable ASCII 0x20..0x7E, plus JL_CHAR_BACKSPACE,
|
|
// JL_CHAR_TAB, JL_CHAR_RETURN, JL_CHAR_ESCAPE, JL_CHAR_DELETE. Shift, caps lock, and
|
|
// the keyboard layout are already applied by the backend, so
|
|
// punctuation and shifted symbols arrive correctly on every port.
|
|
// (The layout is the machine's own on IIgs/Amiga/ST/X68000, DVX's on
|
|
// DVX, and the OS's on the hosted ports; DOS and the PSP's on-screen
|
|
// keyboard use a fixed US layout. On the ST a key held with Alt types
|
|
// nothing.)
|
|
//
|
|
// The queue is refilled by jlInputPoll() -- no extra bring-up is
|
|
// needed. It holds JL_CHAR_QUEUE_SIZE - 1 characters; when full,
|
|
// further characters are dropped (drop-newest) until the app pops.
|
|
// A held key repeats its character on every port (the caller manages
|
|
// repeat policy).
|
|
// A chord types nothing on any port: Ctrl+M is not Return.
|
|
// 7-bit ASCII only; no Unicode / IME. Arrow keys are not characters
|
|
// and stay on the predicate API (jlKeyDown(JL_KEY_UP) etc.).
|
|
// jlWaitForAnyKey empties the queue when it returns -- its dismissing
|
|
// keystroke is consumed by the wait, not delivered as text.
|
|
//
|
|
// Returns int (not a stdint type) deliberately: getchar()-style
|
|
// "byte or -1" is the interface contract consumers expect.
|
|
int jlInputGetChar(void);
|
|
|
|
// Block until the user presses a key: one with a jlKeyE, or one that types
|
|
// a character (punctuation, which has no jlKeyE). Mouse and joystick
|
|
// buttons do not end it, and a key held when the wait starts must be let go
|
|
// and pressed again.
|
|
// Internally polls via jlInputPoll, so per-port jlpInputPoll machinery
|
|
// (including audio-friendly IRQ-driven samplers) keeps working while
|
|
// the wait loop runs.
|
|
void jlWaitForAnyKey(void);
|
|
|
|
// A key two physical keys give (Return and the keypad's Enter, an arrow and
|
|
// the keypad's 8, 2, 4, or 6, the left and right Ctrl or Alt) is down while
|
|
// either is held.
|
|
bool jlKeyDown(jlKeyE key);
|
|
bool jlKeyPressed(jlKeyE key);
|
|
bool jlKeyReleased(jlKeyE key);
|
|
|
|
// True when the platform confirmed an attached mouse at init (DOS: the INT 33h driver reset;
|
|
// Amiga: JoeyLib's input window opened; Win32/Win16: Windows reports one; SDL3/web: SDL reports one;
|
|
// IIgs/ST/X68000/DVX/Haiku/BeOS: always; PSP: never). The IIgs mouse has one button, the ST's and
|
|
// X68000's two.
|
|
bool jlMousePresent(void);
|
|
int16_t jlMouseX(void);
|
|
int16_t jlMouseY(void);
|
|
bool jlMouseDown(jlMouseButtonE button);
|
|
bool jlMousePressed(jlMouseButtonE button);
|
|
bool jlMouseReleased(jlMouseButtonE button);
|
|
|
|
// Every stick has two buttons. On the ST'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: act on button
|
|
// 0 with jlJoyPressed, not jlJoyDown.
|
|
bool jlJoystickConnected(jlJoystickE js);
|
|
int8_t jlJoystickX(jlJoystickE js);
|
|
int8_t jlJoystickY(jlJoystickE js);
|
|
bool jlJoyDown(jlJoystickE js, jlJoyButtonE button);
|
|
bool jlJoyPressed(jlJoystickE js, jlJoyButtonE button);
|
|
bool jlJoyReleased(jlJoystickE js, jlJoyButtonE button);
|
|
|
|
// Set the stick's dead zone (8 at jlInit): axis values within `deadZone` of 0 (on the
|
|
// -127..127 scale) read 0 on the ports with analog sticks (IIgs, DOS,
|
|
// DVX, hosted); use 0 to disable it. On the IIgs and DOS the call also
|
|
// re-centres the stick: the next reading after it is taken as the
|
|
// centre, so the user must hold the stick centred when calling. The
|
|
// IIgs port stops polling after about a second (60 polls) with no stick
|
|
// seen (an absent stick costs ~1 ms a frame of busy-wait) and does NOT
|
|
// re-probe by itself: this call resumes polling. DOS probes the game
|
|
// port again; the Amiga hands the port back to lowlevel.library's own
|
|
// detection. Elsewhere only the dead zone changes.
|
|
void jlJoystickReset(jlJoystickE js, uint8_t deadZone);
|
|
|
|
#endif
|