Repository navigation
Expand file tree
/
Copy pathmodule_api.h
More file actions
192 lines (160 loc) · 7.15 KB
/
Copy pathmodule_api.h
File metadata and controls
192 lines (160 loc) · 7.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#ifndef MODULE_API_H
#define MODULE_API_H
#include <stdint.h>
#include <stdbool.h>
/* Hook indices - must match firmware module_loader.h.
Hook constants are grouped by feature category via an infix tag
(COMBO, KEY, TAPDANCE, LEADER) so the namespace stays self-
documenting as it grows. Lifecycle hooks (INIT/DEINIT) are
universal and use the unqualified MODULE_HOOK_ prefix. The
namespace is flat: a single global hook table indexed by these
values, with one-hook-one-owner enforced at load time. */
/* Combo hooks */
#define MODULE_COMBO_HOOK_SHOULD_TRIGGER 0
#define MODULE_COMBO_HOOK_PROCESS_EVENT 1
#define MODULE_COMBO_HOOK_GET_TERM 2
/* Lifecycle hooks (universal) */
#define MODULE_HOOK_INIT 3
#define MODULE_HOOK_DEINIT 4
/* Combo hooks (continued) */
#define MODULE_COMBO_HOOK_GET_MUST_HOLD 5
#define MODULE_COMBO_HOOK_GET_MUST_TAP 6
#define MODULE_COMBO_HOOK_GET_MUST_PRESS_IN_ORDER 7
#define MODULE_COMBO_HOOK_PROCESS_KEY_RELEASE 8
#define MODULE_COMBO_HOOK_PROCESS_KEY_REPRESS 9
#define MODULE_COMBO_HOOK_REF_FROM_LAYER 10
/* Key processing hooks.
PRE_PROCESS_RECORD overrides pre_process_record_user (strong).
PROCESS_RECORD uses cooperative dispatch (keymap-side call). */
#define MODULE_KEY_HOOK_PRE_PROCESS_RECORD 11
#define MODULE_KEY_HOOK_PROCESS_RECORD 18
#define MODULE_KEY_HOOK_LAYER_STATE_SET 17
/* Tap dance hooks — reserved, not currently dispatched */
#define MODULE_TAPDANCE_HOOK_ON_EACH_TAP 12
#define MODULE_TAPDANCE_HOOK_ON_DANCE_FINISHED 13
#define MODULE_TAPDANCE_HOOK_ON_RESET 14
/* Leader hooks — reserved, not currently dispatched */
#define MODULE_LEADER_HOOK_START 15
#define MODULE_LEADER_HOOK_END 16
/* Lifecycle hooks (universal) */
#define MODULE_HOOK_HOUSEKEEPING 19
#define MODULE_HOOK_SHUTDOWN 20
/* kbsm hooks — for modules that plug into the kbsm
orchestrator (firmware quantum/kbsm.c). A behavior module exports
a single kbsm_t* via this hook, then calls env->kbsm_register
on it in init. */
#define MODULE_KBSM_HOOK_GET_MACHINE 21
#define MODULE_HOOK_MAX 32
/* Value a module's init function must return for the firmware loader
to consider the init call successful. Must match the firmware's
MODULE_INIT_MAGIC in module_loader.h.
Init signature: uint32_t module_init(struct kbsm_env *env)
Deinit signature: uint32_t module_deinit(void)
`env` is a pointer to a firmware-exported callback table (see
kbsm_env_t below). kbsm modules use it to call
kbsm_register, tap_code16, timer_read, etc. Non-behavior modules
(e.g. classic combo hooks) can ignore the argument — it will be NULL
on firmware builds without KEY_BEHAVIOR_SM_ENABLE.
Module code accesses its own .rodata through normal C references;
the loader has already rebased literal-pool addresses to the slot's
runtime location, so no module_base parameter is needed.
Init must return this magic; deinit's return is logged by the firmware but not checked. */
#define MODULE_INIT_MAGIC 0x600DBEEFu
/* mprintf: gated diagnostic print for module code.
Signature is printf-compatible (except the return type is void).
No-op unless the user sets debug_config_user.module = 1 on the
device; otherwise forwards to the firmware's printf (the same
console endpoint used by QMK core).
Why not use QMK's xprintf() directly? xprintf is a preprocessor
macro that expands to printf, not a real symbol, so modules cannot
resolve it through the host's .map-based symbol linkage. Do not
#include quantum/logging/print.h here — it pulls in firmware-only
headers that break the module build.
Output is automatically prefixed with "[mod] " so module console
output is distinguishable from firmware core output. Use mprintf
for routine diagnostics that shouldn't spam the console by default.
Modules that need unconditional output (e.g. fatal error paths) can
still extern-declare printf directly, which bypasses the gate. */
extern void mprintf(const char *fmt, ...);
/* Place the hook table in the .hook_table section */
#define MODULE_HOOK_TABLE __attribute__((section(".hook_table"), used))
/* Minimal type stubs - keep layout-compatible with QMK for accessed APIs. */
typedef struct { const uint16_t *keys; uint16_t keycode; } combo_t;
typedef struct {
uint8_t col;
uint8_t row;
} keypos_t;
typedef enum keyevent_type_t {
TICK_EVENT = 0,
KEY_EVENT = 1,
ENCODER_CW_EVENT = 2,
ENCODER_CCW_EVENT = 3,
COMBO_EVENT = 4,
DIP_SWITCH_ON_EVENT = 5,
DIP_SWITCH_OFF_EVENT = 6,
} keyevent_type_t;
typedef struct {
keypos_t key;
uint16_t time;
keyevent_type_t type;
bool pressed;
} keyevent_t;
typedef struct keyrecord_t {
keyevent_t event;
uint16_t keycode;
} keyrecord_t;
typedef uint16_t layer_state_t;
extern layer_state_t layer_state;
extern layer_state_t default_layer_state;
/* Default combo term if not defined */
#ifndef COMBO_TERM
#define COMBO_TERM 50
#endif
/* ----------------------------------------------------------------------
kbsm module API — for SRAM-loaded behavior modules.
Layout-compatible with firmware quantum/kbsm.h and
keyboards/keychron/common/module/kbsm_env.h. Modules implement an
kbsm_t with handle/tick/reset callbacks, return it via
MODULE_KBSM_HOOK_GET_MACHINE, and kbsm_register it from init.
---------------------------------------------------------------------- */
typedef enum {
KBSM_PHASE_PRE_TAP
/* KBSM_PHASE_POST_TAP and KBSM_PHASE_POST_EXEC are reserved for future
expansion; add them here when kbsm_process_post_tap() gets a real
call site. */
} kbsm_phase_t;
typedef enum {
KBSM_PASS,
KBSM_CONSUME
} kbsm_result_t;
typedef struct kbsm kbsm_t;
struct kbsm {
void *instance;
kbsm_result_t (*handle)(void *self, keyevent_t *event, keyrecord_t *record);
void (*tick)(void *self);
void (*reset)(void *self);
const char *name;
kbsm_phase_t phase;
uint8_t priority;
};
/* Firmware-exported callback table. Module init receives a pointer to
the live g_kbsm_env (or NULL on non-kbsm builds). Store it in
module-local state so handlers can route firmware calls through it. */
typedef struct kbsm_env {
void (*kbsm_register)(kbsm_t *machine);
void (*kbsm_unregister)(kbsm_t *machine);
void (*tap_code16)(uint16_t kc);
void (*register_code16)(uint16_t kc);
void (*unregister_code16)(uint16_t kc);
void (*tap_code)(uint8_t kc);
void (*register_code)(uint8_t kc);
void (*unregister_code)(uint8_t kc);
uint16_t (*timer_read)(void);
uint16_t (*timer_elapsed)(uint16_t since);
uint16_t (*get_record_keycode)(keyrecord_t *r, bool update_layer_cache);
int (*xprintf)(const char *fmt, ...);
void (*send_string)(const char *str); /* ABI v5+ */
void *extension;
uintptr_t module_base;
} kbsm_env_t;
#endif /* MODULE_API_H */