"""Keyboard key codes, modifier flags, and input types."""
from ctypes import Structure, c_int, c_uint, c_uint16, c_uint32
from dataclasses import dataclass
from enum import EJECT, IntEnum, IntFlag
from libretro.ctypes import CBoolArg, CIntArg, TypedFunctionPointer
from .device import InputDeviceState
retro_key = c_int
"""Corresponds to :c:type:`retro_key` in ``libretro.h``."""
RETROK_UNKNOWN = 0
RETROK_FIRST = 0
RETROK_BACKSPACE = 8
RETROK_TAB = 9
RETROK_CLEAR = 12
RETROK_RETURN = 13
RETROK_PAUSE = 19
RETROK_ESCAPE = 27
RETROK_SPACE = 32
RETROK_EXCLAIM = 33
RETROK_QUOTEDBL = 34
RETROK_HASH = 35
RETROK_DOLLAR = 36
RETROK_AMPERSAND = 38
RETROK_QUOTE = 39
RETROK_LEFTPAREN = 40
RETROK_RIGHTPAREN = 41
RETROK_ASTERISK = 42
RETROK_PLUS = 43
RETROK_COMMA = 44
RETROK_MINUS = 45
RETROK_PERIOD = 46
RETROK_SLASH = 47
RETROK_0 = 48
RETROK_1 = 49
RETROK_2 = 50
RETROK_3 = 51
RETROK_4 = 52
RETROK_5 = 53
RETROK_6 = 54
RETROK_7 = 55
RETROK_8 = 56
RETROK_9 = 57
RETROK_COLON = 58
RETROK_SEMICOLON = 59
RETROK_LESS = 60
RETROK_EQUALS = 61
RETROK_GREATER = 62
RETROK_QUESTION = 63
RETROK_AT = 64
RETROK_LEFTBRACKET = 91
RETROK_BACKSLASH = 92
RETROK_RIGHTBRACKET = 93
RETROK_CARET = 94
RETROK_UNDERSCORE = 95
RETROK_BACKQUOTE = 96
RETROK_a = 97
RETROK_b = 98
RETROK_c = 99
RETROK_d = 100
RETROK_e = 101
RETROK_f = 102
RETROK_g = 103
RETROK_h = 104
RETROK_i = 105
RETROK_j = 106
RETROK_k = 107
RETROK_l = 108
RETROK_m = 109
RETROK_n = 110
RETROK_o = 111
RETROK_p = 112
RETROK_q = 113
RETROK_r = 114
RETROK_s = 115
RETROK_t = 116
RETROK_u = 117
RETROK_v = 118
RETROK_w = 119
RETROK_x = 120
RETROK_y = 121
RETROK_z = 122
RETROK_LEFTBRACE = 123
RETROK_BAR = 124
RETROK_RIGHTBRACE = 125
RETROK_TILDE = 126
RETROK_DELETE = 127
RETROK_KP0 = 256
RETROK_KP1 = 257
RETROK_KP2 = 258
RETROK_KP3 = 259
RETROK_KP4 = 260
RETROK_KP5 = 261
RETROK_KP6 = 262
RETROK_KP7 = 263
RETROK_KP8 = 264
RETROK_KP9 = 265
RETROK_KP_PERIOD = 266
RETROK_KP_DIVIDE = 267
RETROK_KP_MULTIPLY = 268
RETROK_KP_MINUS = 269
RETROK_KP_PLUS = 270
RETROK_KP_ENTER = 271
RETROK_KP_EQUALS = 272
RETROK_UP = 273
RETROK_DOWN = 274
RETROK_RIGHT = 275
RETROK_LEFT = 276
RETROK_INSERT = 277
RETROK_HOME = 278
RETROK_END = 279
RETROK_PAGEUP = 280
RETROK_PAGEDOWN = 281
RETROK_F1 = 282
RETROK_F2 = 283
RETROK_F3 = 284
RETROK_F4 = 285
RETROK_F5 = 286
RETROK_F6 = 287
RETROK_F7 = 288
RETROK_F8 = 289
RETROK_F9 = 290
RETROK_F10 = 291
RETROK_F11 = 292
RETROK_F12 = 293
RETROK_F13 = 294
RETROK_F14 = 295
RETROK_F15 = 296
RETROK_NUMLOCK = 300
RETROK_CAPSLOCK = 301
RETROK_SCROLLOCK = 302
RETROK_RSHIFT = 303
RETROK_LSHIFT = 304
RETROK_RCTRL = 305
RETROK_LCTRL = 306
RETROK_RALT = 307
RETROK_LALT = 308
RETROK_RMETA = 309
RETROK_LMETA = 310
RETROK_LSUPER = 311
RETROK_RSUPER = 312
RETROK_MODE = 313
RETROK_COMPOSE = 314
RETROK_HELP = 315
RETROK_PRINT = 316
RETROK_SYSREQ = 317
RETROK_BREAK = 318
RETROK_MENU = 319
RETROK_POWER = 320
RETROK_EURO = 321
RETROK_UNDO = 322
RETROK_OEM_102 = 323
RETROK_BROWSER_BACK = 324
RETROK_BROWSER_FORWARD = 325
RETROK_BROWSER_REFRESH = 326
RETROK_BROWSER_STOP = 327
RETROK_BROWSER_SEARCH = 328
RETROK_BROWSER_FAVORITES = 329
RETROK_BROWSER_HOME = 330
RETROK_VOLUME_MUTE = 331
RETROK_VOLUME_DOWN = 332
RETROK_VOLUME_UP = 333
RETROK_MEDIA_NEXT = 334
RETROK_MEDIA_PREV = 335
RETROK_MEDIA_STOP = 336
RETROK_MEDIA_PLAY_PAUSE = 337
RETROK_LAUNCH_MAIL = 338
RETROK_LAUNCH_MEDIA = 339
RETROK_LAUNCH_APP1 = 340
RETROK_LAUNCH_APP2 = 341
RETROK_LAST = RETROK_LAUNCH_APP2 + 1
RETROK_DUMMY = 0x7FFFFFFF
retro_mod = c_int
RETROKMOD_NONE = 0x0000
RETROKMOD_SHIFT = 0x01
RETROKMOD_CTRL = 0x02
RETROKMOD_ALT = 0x04
RETROKMOD_META = 0x08
RETROKMOD_NUMLOCK = 0x10
RETROKMOD_CAPSLOCK = 0x20
RETROKMOD_SCROLLOCK = 0x40
RETROKMOD_DUMMY = 0x7FFFFFFF
[docs]
class Key(IntEnum, boundary=EJECT):
"""
Enumeration of keyboard key codes.
Corresponds to the ``RETROK_*`` constants in ``libretro.h``.
>>> from libretro.api.input import Key
>>> Key.A
<Key.A: 97>
"""
UNKNOWN = RETROK_UNKNOWN
BACKSPACE = RETROK_BACKSPACE
TAB = RETROK_TAB
CLEAR = RETROK_CLEAR
RETURN = RETROK_RETURN
PAUSE = RETROK_PAUSE
ESCAPE = RETROK_ESCAPE
SPACE = RETROK_SPACE
EXCLAIM = RETROK_EXCLAIM
QUOTEDBL = RETROK_QUOTEDBL
HASH = RETROK_HASH
DOLLAR = RETROK_DOLLAR
AMPERSAND = RETROK_AMPERSAND
QUOTE = RETROK_QUOTE
LEFTPAREN = RETROK_LEFTPAREN
RIGHTPAREN = RETROK_RIGHTPAREN
ASTERISK = RETROK_ASTERISK
PLUS = RETROK_PLUS
COMMA = RETROK_COMMA
MINUS = RETROK_MINUS
PERIOD = RETROK_PERIOD
SLASH = RETROK_SLASH
Zero = RETROK_0
One = RETROK_1
Two = RETROK_2
Three = RETROK_3
Four = RETROK_4
Five = RETROK_5
Six = RETROK_6
Seven = RETROK_7
Eight = RETROK_8
Nine = RETROK_9
COLON = RETROK_COLON
SEMICOLON = RETROK_SEMICOLON
LESS = RETROK_LESS
EQUALS = RETROK_EQUALS
GREATER = RETROK_GREATER
QUESTION = RETROK_QUESTION
AT = RETROK_AT
LEFTBRACKET = RETROK_LEFTBRACKET
BACKSLASH = RETROK_BACKSLASH
RIGHTBRACKET = RETROK_RIGHTBRACKET
CARET = RETROK_CARET
UNDERSCORE = RETROK_UNDERSCORE
BACKQUOTE = RETROK_BACKQUOTE
A = RETROK_a
B = RETROK_b
C = RETROK_c
D = RETROK_d
E = RETROK_e
F = RETROK_f
G = RETROK_g
H = RETROK_h
I = RETROK_i
J = RETROK_j
K = RETROK_k
L = RETROK_l
M = RETROK_m
N = RETROK_n
O = RETROK_o
P = RETROK_p
Q = RETROK_q
R = RETROK_r
S = RETROK_s
T = RETROK_t
U = RETROK_u
V = RETROK_v
W = RETROK_w
X = RETROK_x
Y = RETROK_y
Z = RETROK_z
LEFTBRACE = RETROK_LEFTBRACE
BAR = RETROK_BAR
RIGHTBRACE = RETROK_RIGHTBRACE
TILDE = RETROK_TILDE
DELETE = RETROK_DELETE
KP0 = RETROK_KP0
KP1 = RETROK_KP1
KP2 = RETROK_KP2
KP3 = RETROK_KP3
KP4 = RETROK_KP4
KP5 = RETROK_KP5
KP6 = RETROK_KP6
KP7 = RETROK_KP7
KP8 = RETROK_KP8
KP9 = RETROK_KP9
KP_PERIOD = RETROK_KP_PERIOD
KP_DIVIDE = RETROK_KP_DIVIDE
KP_MULTIPLY = RETROK_KP_MULTIPLY
KP_MINUS = RETROK_KP_MINUS
KP_PLUS = RETROK_KP_PLUS
KP_ENTER = RETROK_KP_ENTER
KP_EQUALS = RETROK_KP_EQUALS
UP = RETROK_UP
DOWN = RETROK_DOWN
RIGHT = RETROK_RIGHT
LEFT = RETROK_LEFT
INSERT = RETROK_INSERT
HOME = RETROK_HOME
END = RETROK_END
PAGEUP = RETROK_PAGEUP
PAGEDOWN = RETROK_PAGEDOWN
F1 = RETROK_F1
F2 = RETROK_F2
F3 = RETROK_F3
F4 = RETROK_F4
F5 = RETROK_F5
F6 = RETROK_F6
F7 = RETROK_F7
F8 = RETROK_F8
F9 = RETROK_F9
F10 = RETROK_F10
F11 = RETROK_F11
F12 = RETROK_F12
F13 = RETROK_F13
F14 = RETROK_F14
F15 = RETROK_F15
NUMLOCK = RETROK_NUMLOCK
CAPSLOCK = RETROK_CAPSLOCK
SCROLLOCK = RETROK_SCROLLOCK
RSHIFT = RETROK_RSHIFT
LSHIFT = RETROK_LSHIFT
RCTRL = RETROK_RCTRL
LCTRL = RETROK_LCTRL
RALT = RETROK_RALT
LALT = RETROK_LALT
RMETA = RETROK_RMETA
LMETA = RETROK_LMETA
LSUPER = RETROK_LSUPER
RSUPER = RETROK_RSUPER
MODE = RETROK_MODE
COMPOSE = RETROK_COMPOSE
HELP = RETROK_HELP
PRINT = RETROK_PRINT
SYSREQ = RETROK_SYSREQ
BREAK = RETROK_BREAK
MENU = RETROK_MENU
POWER = RETROK_POWER
EURO = RETROK_EURO
UNDO = RETROK_UNDO
OEM_102 = RETROK_OEM_102
BROWSER_BACK = RETROK_BROWSER_BACK
BROWSER_FORWARD = RETROK_BROWSER_FORWARD
BROWSER_REFRESH = RETROK_BROWSER_REFRESH
BROWSER_STOP = RETROK_BROWSER_STOP
BROWSER_SEARCH = RETROK_BROWSER_SEARCH
BROWSER_FAVORITES = RETROK_BROWSER_FAVORITES
BROWSER_HOME = RETROK_BROWSER_HOME
VOLUME_MUTE = RETROK_VOLUME_MUTE
VOLUME_DOWN = RETROK_VOLUME_DOWN
VOLUME_UP = RETROK_VOLUME_UP
MEDIA_NEXT = RETROK_MEDIA_NEXT
MEDIA_PREV = RETROK_MEDIA_PREV
MEDIA_STOP = RETROK_MEDIA_STOP
MEDIA_PLAY_PAUSE = RETROK_MEDIA_PLAY_PAUSE
LAUNCH_MAIL = RETROK_LAUNCH_MAIL
LAUNCH_MEDIA = RETROK_LAUNCH_MEDIA
LAUNCH_APP1 = RETROK_LAUNCH_APP1
LAUNCH_APP2 = RETROK_LAUNCH_APP2
@property
def is_modifier(self):
""":returns: :obj:`True` if this key represents one of the modifiers defined in :class:`.KeyModifier`."""
return self in (
Key.LCTRL,
Key.RCTRL,
Key.LSHIFT,
Key.RSHIFT,
Key.LALT,
Key.RALT,
Key.LMETA,
Key.RMETA,
Key.NUMLOCK,
Key.CAPSLOCK,
Key.SCROLLOCK,
)
[docs]
class KeyModifier(IntFlag):
"""
Flags for key modifiers.
Corresponds to the ``RETROKMOD_*`` constants in ``libretro.h``.
>>> from libretro.api.input import KeyModifier
>>> KeyModifier.SHIFT
<KeyModifier.SHIFT: 1>
"""
NONE = RETROKMOD_NONE
SHIFT = RETROKMOD_SHIFT
CTRL = RETROKMOD_CTRL
ALT = RETROKMOD_ALT
META = RETROKMOD_META
NUMLOCK = RETROKMOD_NUMLOCK
CAPSLOCK = RETROKMOD_CAPSLOCK
SCROLLOCK = RETROKMOD_SCROLLOCK
[docs]
@dataclass(frozen=True, slots=True)
class KeyboardState(InputDeviceState):
"""
Snapshot of the keyboard state.
Each field corresponds to a key's pressed state.
>>> from libretro.api.input import KeyboardState
>>> state = KeyboardState()
>>> state.space
False
"""
backspace: bool = False
tab: bool = False
clear: bool = False
return_key: bool = False
pause: bool = False
escape: bool = False
space: bool = False
exclaim: bool = False
quotedbl: bool = False
hash: bool = False
dollar: bool = False
ampersand: bool = False
quote: bool = False
leftparen: bool = False
rightparen: bool = False
asterisk: bool = False
plus: bool = False
comma: bool = False
minus: bool = False
period: bool = False
slash: bool = False
zero: bool = False
one: bool = False
two: bool = False
three: bool = False
four: bool = False
five: bool = False
six: bool = False
seven: bool = False
eight: bool = False
nine: bool = False
colon: bool = False
semicolon: bool = False
less: bool = False
equals: bool = False
greater: bool = False
question: bool = False
at: bool = False
leftbracket: bool = False
backslash: bool = False
rightbracket: bool = False
caret: bool = False
underscore: bool = False
backquote: bool = False
a: bool = False
b: bool = False
c: bool = False
d: bool = False
e: bool = False
f: bool = False
g: bool = False
h: bool = False
i: bool = False
j: bool = False
k: bool = False
l: bool = False
m: bool = False
n: bool = False
o: bool = False
p: bool = False
q: bool = False
r: bool = False
s: bool = False
t: bool = False
u: bool = False
v: bool = False
w: bool = False
x: bool = False
y: bool = False
z: bool = False
leftbrace: bool = False
bar: bool = False
rightbrace: bool = False
tilde: bool = False
delete: bool = False
kp0: bool = False
kp1: bool = False
kp2: bool = False
kp3: bool = False
kp4: bool = False
kp5: bool = False
kp6: bool = False
kp7: bool = False
kp8: bool = False
kp9: bool = False
kp_period: bool = False
kp_divide: bool = False
kp_multiply: bool = False
kp_minus: bool = False
kp_plus: bool = False
kp_enter: bool = False
kp_equals: bool = False
up: bool = False
down: bool = False
right: bool = False
left: bool = False
insert: bool = False
home: bool = False
end: bool = False
pageup: bool = False
pagedown: bool = False
f1: bool = False
f2: bool = False
f3: bool = False
f4: bool = False
f5: bool = False
f6: bool = False
f7: bool = False
f8: bool = False
f9: bool = False
f10: bool = False
f11: bool = False
f12: bool = False
f13: bool = False
f14: bool = False
f15: bool = False
numlock: bool = False
capslock: bool = False
scrolllock: bool = False
rshift: bool = False
lshift: bool = False
rctrl: bool = False
lctrl: bool = False
ralt: bool = False
lalt: bool = False
rmeta: bool = False
lmeta: bool = False
lsuper: bool = False
rsuper: bool = False
mode: bool = False
compose: bool = False
help: bool = False
print: bool = False
sysreq: bool = False
break_key: bool = False
menu: bool = False
power: bool = False
euro: bool = False
oem_102: bool = False
browser_back: bool = False
browser_forward: bool = False
browser_refresh: bool = False
browser_stop: bool = False
browser_search: bool = False
browser_favorites: bool = False
browser_home: bool = False
volume_mute: bool = False
volume_down: bool = False
volume_up: bool = False
media_next: bool = False
media_prev: bool = False
media_stop: bool = False
media_play_pause: bool = False
launch_mail: bool = False
launch_media: bool = False
launch_app1: bool = False
launch_app2: bool = False
[docs]
def __getitem__(self, item: int | Key) -> bool:
"""
Get the pressed state of the given key.
:param item: A :class:`Key` or an equivalent key code.
:return: :obj:`True` if the given :class:`Key` or key code is currently pressed.
:raises KeyError: If ``item`` isn't a valid key code.
>>> from libretro.api.input import KeyboardState, Key
>>> state = KeyboardState(a=True)
>>> state[Key.A]
True
>>> state[Key.B]
False
"""
match item:
# Special cases due to overlap with Python keywords
case Key.RETURN:
return self.return_key
case Key.BREAK:
return self.break_key
case Key():
return getattr(self, item.name.lower())
case int(i) if i in Key:
return getattr(self, Key(i).name.lower())
case _:
raise KeyError(f"Invalid key: {item}")
retro_keyboard_event_t = TypedFunctionPointer[
None,
[
CBoolArg,
CIntArg[c_uint],
CIntArg[c_uint32],
CIntArg[c_uint16],
],
]
"""
Notify the core of a single keyboard event.
Registered by the :term:`core` and called by the :term:`frontend`
when a key is pressed, released, or generates a text character.
The keycode and character may be reported independently
(e.g. a single keypress can yield multiple text characters,
or a character can arrive without a corresponding keycode).
:param down: :obj:`True` if the key is being pressed,
:obj:`False` if it is being released.
:param keycode: The :class:`Key` of the key involved,
or :attr:`Key.UNKNOWN` if only a character event is being reported.
:param character: A UTF-32 codepoint generated by this event,
or ``0`` if no character was generated.
:param key_modifiers: A bitmask of :class:`KeyModifier` flags
that were active when the event occurred.
Corresponds to :c:type:`retro_keyboard_event_t` in ``libretro.h``.
.. seealso::
:meth:`.InputDriver.keyboard_event`
The suggested entry point for this registered callback in libretro.py.
"""
[docs]
@dataclass(init=False, slots=True)
class retro_keyboard_callback(Structure):
"""
Function registered by a :term:`core` to receive keyboard events.
Can be invoked directly as a callable.
Corresponds to :c:type:`retro_keyboard_callback` in ``libretro.h``.
"""
callback: retro_keyboard_event_t | None
_fields_ = (("callback", retro_keyboard_event_t),)
[docs]
def __deepcopy__(self, _):
"""
Return a copy of this object.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_keyboard_callback(callback=self.callback)
[docs]
def __call__(
self, pressed: bool, keycode: Key, character: int | str | bytes, key_modifiers: KeyModifier
) -> None:
"""
Invoke the keyboard event callback.
Does nothing if :attr:`callback` is :obj:`None`.
:param pressed: Whether the key was pressed or released.
:param keycode: The :class:`Key` code.
:param character: A single character as a UTF-32 codepoint or a single-character :class:`str` or :class:`bytes` object.
:param key_modifiers: Active :class:`KeyModifier` flags.
"""
match character:
case int():
# UTF-32 codepoint
char = character
case str() | bytes() if len(character) == 1:
char = ord(character)
case _:
raise ValueError(
f"Expected character to be an int, a single-character string, or a single byte, got {character!r}"
)
if self.callback:
self.callback(pressed, keycode, char, key_modifiers)
__all__ = [
"Key",
"KeyModifier",
"KeyboardState",
"retro_keyboard_event_t",
"retro_keyboard_callback",
"retro_key",
"retro_mod",
]