upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/main/keyboard.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/keyboard.c')
-rw-r--r--main/keyboard.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/main/keyboard.c b/main/keyboard.c
new file mode 100644
index 0000000..d16135f
--- /dev/null
+++ b/main/keyboard.c
@@ -0,0 +1,186 @@
1#include "keyboard.h"
2#include <string.h>
3
4static const char *s_alpha_lower[] = {
5 "qwertyuiop",
6 "asdfghjkl",
7 "\001zxcvbnm\b",
8 "\002\003\004"
9};
10
11static const char *s_alpha_upper[] = {
12 "QWERTYUIOP",
13 "ASDFGHJKL",
14 "\001ZXCVBNM\b",
15 "\002\003\004"
16};
17
18static const char *s_numsym[] = {
19 "1234567890",
20 "-/:;()$&@\"",
21 "\001.,?!'\\b",
22 "\002\003\004"
23};
24
25#define CTRL_SHIFT '\001'
26#define CTRL_LAYER '\002'
27#define CTRL_SPACE '\003'
28#define CTRL_DONE '\004'
29#define CTRL_BS '\b'
30
31static kb_layout_t s_layout = {
32 .key_w = 28,
33 .key_h = 36,
34 .key_gap = 2,
35 .start_y = 70,
36 .screen_w = 320,
37 .row_count = 4,
38};
39
40void kb_state_init(kb_state_t *st) {
41 if (!st) return;
42 memset(st, 0, sizeof(*st));
43 st->layer = KB_ALPHA_LOWER;
44 st->reveal = false;
45}
46
47void kb_set_layout(const kb_layout_t *layout) {
48 if (layout) s_layout = *layout;
49}
50
51const kb_layout_t *kb_get_layout(void) {
52 return &s_layout;
53}
54
55static const char **get_layer(kb_layer_t layer) {
56 switch (layer) {
57 case KB_ALPHA_UPPER: return s_alpha_upper;
58 case KB_NUMSYM: return s_numsym;
59 default: return s_alpha_lower;
60 }
61}
62
63int kb_get_row_keys(int row, kb_layer_t layer, const char **keys_out) {
64 if (row < 0 || row >= s_layout.row_count) {
65 *keys_out = NULL;
66 return 0;
67 }
68 const char **layer_rows = get_layer(layer);
69 const char *row_str = layer_rows[row];
70 *keys_out = row_str;
71 return (int)strlen(row_str);
72}
73
74static int row_x_offset(int row, int total_keys) {
75 int kw = s_layout.key_w;
76 int gap = s_layout.key_gap;
77 int total_w = total_keys * kw + (total_keys - 1) * gap;
78 int margin = (s_layout.screen_w - total_w) / 2;
79 if (margin < 2) margin = 2;
80 switch (row) {
81 case 0: return margin;
82 case 1: return margin + kw / 2;
83 case 2: return margin + kw;
84 case 3: return margin;
85 default: return margin;
86 }
87}
88
89static int key_width_at(int row, int col, int total_keys) {
90 int kw = s_layout.key_w;
91 if (row == 3) {
92 int gap = s_layout.key_gap;
93 int margin = row_x_offset(3, total_keys);
94 int available = s_layout.screen_w - margin * 2;
95 int side_w = (available - gap) / 4;
96 if (col == 0) return side_w;
97 if (col == total_keys - 1) return side_w;
98 return available - side_w * 2 - gap * 2;
99 }
100 return kw;
101}
102
103kb_result_t kb_hit_test(int tx, int ty, kb_layer_t layer) {
104 kb_result_t result = {KB_ACTION_NONE, 0};
105 int sy = s_layout.start_y;
106 int kw = s_layout.key_w;
107 int kh = s_layout.key_h;
108 int gap = s_layout.key_gap;
109
110 if (ty < sy || ty >= sy + s_layout.row_count * (kh + gap)) {
111 return result;
112 }
113
114 int row = (ty - sy) / (kh + gap);
115 if (row < 0 || row >= s_layout.row_count) return result;
116
117 const char *row_str;
118 int total_keys = kb_get_row_keys(row, layer, &row_str);
119 if (total_keys == 0) return result;
120
121 int x_off = row_x_offset(row, total_keys);
122 int cx = x_off;
123
124 for (int col = 0; col < total_keys; col++) {
125 int key_w = key_width_at(row, col, total_keys);
126 if (tx >= cx && tx < cx + key_w) {
127 char c = row_str[col];
128 if (c == CTRL_SHIFT) {
129 result.action = KB_ACTION_SHIFT;
130 } else if (c == CTRL_LAYER) {
131 result.action = KB_ACTION_LAYER;
132 } else if (c == CTRL_SPACE) {
133 result.action = KB_ACTION_SPACE;
134 result.ch = ' ';
135 } else if (c == CTRL_DONE) {
136 result.action = KB_ACTION_DONE;
137 } else if (c == CTRL_BS) {
138 result.action = KB_ACTION_BACKSPACE;
139 } else {
140 result.action = KB_ACTION_CHAR;
141 result.ch = c;
142 }
143 return result;
144 }
145 cx += key_w + gap;
146 }
147
148 return result;
149}
150
151void kb_apply(kb_state_t *st, kb_result_t result) {
152 if (!st || result.action == KB_ACTION_NONE) return;
153
154 switch (result.action) {
155 case KB_ACTION_CHAR:
156 if (st->cursor < KB_INPUT_MAX) {
157 st->input[st->cursor++] = result.ch;
158 st->input[st->cursor] = '\0';
159 }
160 break;
161 case KB_ACTION_BACKSPACE:
162 if (st->cursor > 0) {
163 st->cursor--;
164 st->input[st->cursor] = '\0';
165 }
166 break;
167 case KB_ACTION_SHIFT:
168 if (st->layer == KB_ALPHA_LOWER) st->layer = KB_ALPHA_UPPER;
169 else if (st->layer == KB_ALPHA_UPPER) st->layer = KB_ALPHA_LOWER;
170 break;
171 case KB_ACTION_LAYER:
172 if (st->layer == KB_NUMSYM) st->layer = KB_ALPHA_LOWER;
173 else st->layer = KB_NUMSYM;
174 break;
175 case KB_ACTION_SPACE:
176 if (st->cursor < KB_INPUT_MAX) {
177 st->input[st->cursor++] = ' ';
178 st->input[st->cursor] = '\0';
179 }
180 break;
181 case KB_ACTION_DONE:
182 break;
183 default:
184 break;
185 }
186}