emu: remember page editor cursor position in memview
[zymosis.git] / src / ZXEmuT / jimapi_sdlkeys.c
blobf75ecf93e6f9b0cba636495eedc350941d21174f
1 /***************************************************************************
3 * ZXEmuT -- ZX Spectrum Emulator with Tcl scripting
5 * Copyright (C) 2012-2022 Ketmar Dark <ketmar@ketmar.no-ip.org>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **************************************************************************/
20 static const struct {
21 uint16_t code;
22 const char *name;
23 } sdlKeyNames[] = {
24 {SDLK_BACKSPACE, "BACKSPACE"},
25 {SDLK_TAB, "TAB"},
26 {SDLK_CLEAR, "CLEAR"},
27 {SDLK_RETURN, "RETURN"},
28 {SDLK_RETURN, "ENTER"},
29 {SDLK_PAUSE, "PAUSE"},
30 {SDLK_ESCAPE, "ESCAPE"},
31 {SDLK_ESCAPE, "ESC"},
32 {SDLK_SPACE, "SPACE"},
33 {SDLK_EXCLAIM, "EXCLAIM"},
34 {SDLK_EXCLAIM, "!"},
35 {SDLK_QUOTEDBL, "QUOTEDBL"},
36 {SDLK_QUOTEDBL, "\""},
37 {SDLK_HASH, "HASH"},
38 {SDLK_HASH, "#"},
39 {SDLK_DOLLAR, "DOLLAR"},
40 {SDLK_DOLLAR, "$"},
41 {SDLK_AMPERSAND, "AMPERSAND"},
42 {SDLK_AMPERSAND, "&"},
43 {SDLK_QUOTE, "QUOTE"},
44 {SDLK_QUOTE, "'"},
45 {SDLK_LEFTPAREN, "LEFTPAREN"},
46 {SDLK_LEFTPAREN, "("},
47 {SDLK_RIGHTPAREN, "RIGHTPAREN"},
48 {SDLK_RIGHTPAREN, ")"},
49 {SDLK_ASTERISK, "ASTERISK"},
50 {SDLK_ASTERISK, "*"},
51 {SDLK_PLUS, "PLUS"},
52 {SDLK_PLUS, "+"},
53 {SDLK_COMMA, "COMMA"},
54 {SDLK_COMMA, ","},
55 {SDLK_MINUS, "MINUS"},
56 {SDLK_MINUS, "-"},
57 {SDLK_PERIOD, "PERIOD"},
58 {SDLK_PERIOD, "."},
59 {SDLK_SLASH, "SLASH"},
60 {SDLK_SLASH, "/"},
61 {SDLK_0, "0"},
62 {SDLK_1, "1"},
63 {SDLK_2, "2"},
64 {SDLK_3, "3"},
65 {SDLK_4, "4"},
66 {SDLK_5, "5"},
67 {SDLK_6, "6"},
68 {SDLK_7, "7"},
69 {SDLK_8, "8"},
70 {SDLK_9, "9"},
71 {SDLK_COLON, "COLON"},
72 {SDLK_COLON, "."},
73 {SDLK_SEMICOLON, "SEMICOLON"},
74 {SDLK_SEMICOLON, ";"},
75 {SDLK_LESS, "LESS"},
76 {SDLK_LESS, "<"},
77 {SDLK_EQUALS, "EQUALS"},
78 {SDLK_EQUALS, "="},
79 {SDLK_GREATER, "GREATER"},
80 {SDLK_GREATER, ">"},
81 {SDLK_QUESTION, "QUESTION"},
82 {SDLK_QUESTION, "?"},
83 {SDLK_AT, "AT"},
84 {SDLK_AT, "@"},
85 {SDLK_LEFTBRACKET, "LEFTBRACKET"},
86 {SDLK_LEFTBRACKET, "["},
87 {SDLK_BACKSLASH, "BACKSLASH"},
88 {SDLK_BACKSLASH, "\\"},
89 {SDLK_RIGHTBRACKET, "RIGHTBRACKET"},
90 {SDLK_RIGHTBRACKET, "]"},
91 {SDLK_CARET, "CARET"},
92 {SDLK_UNDERSCORE, "UNDERSCORE"},
93 {SDLK_UNDERSCORE, "_"},
94 {SDLK_BACKQUOTE, "BACKQUOTE"},
95 {SDLK_BACKQUOTE, "`"},
96 {SDLK_BACKQUOTE, "TILDA"},
97 {SDLK_BACKQUOTE, "~"},
98 {SDLK_a, "a"},
99 {SDLK_b, "b"},
100 {SDLK_c, "c"},
101 {SDLK_d, "d"},
102 {SDLK_e, "e"},
103 {SDLK_f, "f"},
104 {SDLK_g, "g"},
105 {SDLK_h, "h"},
106 {SDLK_i, "i"},
107 {SDLK_j, "j"},
108 {SDLK_k, "k"},
109 {SDLK_l, "l"},
110 {SDLK_m, "m"},
111 {SDLK_n, "n"},
112 {SDLK_o, "o"},
113 {SDLK_p, "p"},
114 {SDLK_q, "q"},
115 {SDLK_r, "r"},
116 {SDLK_s, "s"},
117 {SDLK_t, "t"},
118 {SDLK_u, "u"},
119 {SDLK_v, "v"},
120 {SDLK_w, "w"},
121 {SDLK_x, "x"},
122 {SDLK_y, "y"},
123 {SDLK_z, "z"},
124 {SDLK_DELETE, "DELETE"},
125 {SDLK_DELETE, "DEL"},
127 {SDLK_KP0, "KP0"},
128 {SDLK_KP1, "KP1"},
129 {SDLK_KP2, "KP2"},
130 {SDLK_KP3, "KP3"},
131 {SDLK_KP4, "KP4"},
132 {SDLK_KP5, "KP5"},
133 {SDLK_KP6, "KP6"},
134 {SDLK_KP7, "KP7"},
135 {SDLK_KP8, "KP8"},
136 {SDLK_KP9, "KP9"},
137 {SDLK_KP_PERIOD, "KP_PERIOD"},
138 {SDLK_KP_DIVIDE, "KP_DIVIDE"},
139 {SDLK_KP_MULTIPLY, "KP_MULTIPLY"},
140 {SDLK_KP_MINUS, "KP_MINUS"},
141 {SDLK_KP_PLUS, "KP_PLUS"},
142 {SDLK_KP_ENTER, "KP_ENTER"},
143 {SDLK_KP_EQUALS, "KP_EQUALS"},
145 {SDLK_UP, "UP"},
146 {SDLK_DOWN, "DOWN"},
147 {SDLK_RIGHT, "RIGHT"},
148 {SDLK_LEFT, "LEFT"},
149 {SDLK_INSERT, "INSERT"},
150 {SDLK_HOME, "HOME"},
151 {SDLK_END, "END"},
152 {SDLK_PAGEUP, "PAGEUP"},
153 {SDLK_PAGEUP, "PGUP"},
154 {SDLK_PAGEDOWN, "PAGEDOWN"},
155 {SDLK_PAGEDOWN, "PGDOWN"},
156 {SDLK_PAGEDOWN, "PGDN"},
158 {SDLK_F1, "F1"},
159 {SDLK_F2, "F2"},
160 {SDLK_F3, "F3"},
161 {SDLK_F4, "F4"},
162 {SDLK_F5, "F5"},
163 {SDLK_F6, "F6"},
164 {SDLK_F7, "F7"},
165 {SDLK_F8, "F8"},
166 {SDLK_F9, "F9"},
167 {SDLK_F10, "F10"},
168 {SDLK_F11, "F11"},
169 {SDLK_F12, "F12"},
170 {SDLK_F13, "F13"},
171 {SDLK_F14, "F14"},
172 {SDLK_F15, "F15"},
174 {SDLK_NUMLOCK, "NUMLOCK"},
175 {SDLK_CAPSLOCK, "CAPSLOCK"},
176 {SDLK_SCROLLOCK, "SCROLLOCK"},
178 {SDLK_RSHIFT, "RSHIFT"},
179 {SDLK_LSHIFT, "LSHIFT"},
180 {SDLK_RCTRL, "RCTRL"},
181 {SDLK_LCTRL, "LCTRL"},
182 {SDLK_RALT, "RALT"},
183 {SDLK_LALT, "LALT"},
184 {SDLK_RMETA, "RMETA"},
185 {SDLK_LMETA, "LMETA"},
186 {SDLK_LSUPER, "LHYPER"},
187 {SDLK_RSUPER, "LHYPER"},
188 {SDLK_MODE, "MODE"},
189 {SDLK_COMPOSE, "COMPOSE"},
191 {SDLK_HELP, "HELP"},
192 {SDLK_PRINT, "PRINT"},
193 {SDLK_SYSREQ, "SYSREQ"},
194 {SDLK_BREAK, "BREAK"},
195 {SDLK_MENU, "MENU"},
196 {SDLK_POWER, "POWER"},
197 {SDLK_EURO, "EURO"},
198 {SDLK_UNDO, "UNDO"},
200 {0, NULL}
204 ////////////////////////////////////////////////////////////////////////////////
205 SDLJimBinding *sdlJimBindings = NULL;
208 ////////////////////////////////////////////////////////////////////////////////
209 static void zxBindSDLK (uint16_t sdlk, const char *zxkeyname, const char *zxkeyname1) {
210 int keyno = zxFindKeyByName(zxkeyname), keyno1 = zxFindKeyByName(zxkeyname1);
212 zxKeyBinds[sdlk] = 0;
213 if (keyno >= 0 || keyno1 >= 0) {
214 if (keyno < 0) { keyno = keyno1; keyno1 = -1; }
215 if (keyno >= 0) zxKeyBinds[sdlk] = (zxKeyInfo[keyno].port<<8)|zxKeyInfo[keyno].mask;
216 if (keyno1 >= 0) {
217 uint32_t n = (zxKeyInfo[keyno1].port<<8)|zxKeyInfo[keyno1].mask;
219 zxKeyBinds[sdlk] |= n<<16;
225 ////////////////////////////////////////////////////////////////////////////////
226 static uint16_t sdlFindKeyCode (const char *name) {
227 if (name != NULL && name[0]) {
228 for (unsigned f = 0; sdlKeyNames[f].name != NULL; ++f) {
229 if (strcasecmp(name, sdlKeyNames[f].name) == 0) return sdlKeyNames[f].code;
232 return 0;
236 static const char *sdlFindKeyName (uint16_t code) {
237 for (unsigned f = 0; sdlKeyNames[f].name != NULL; ++f) {
238 if (sdlKeyNames[f].code == code) return sdlKeyNames[f].name;
240 return "UNKNOWN";
244 static const char *sdlBuildEmacsKeyName (SDL_KeyboardEvent *key) {
245 static char buf[128];
246 if (!key) { buf[0] = 0; return buf; }
247 size_t bpos = 0;
248 // put modifiers
249 if (key->keysym.mod&KMOD_CTRL) { if (bpos) buf[bpos++] = '-'; buf[bpos++] = 'C'; }
250 if (key->keysym.mod&KMOD_SHIFT) { if (bpos) buf[bpos++] = '-'; buf[bpos++] = 'S'; }
251 if (key->keysym.mod&KMOD_ALT) { if (bpos) buf[bpos++] = '-'; buf[bpos++] = 'M'; }
252 if (bpos) buf[bpos++] = '-';
253 buf[bpos] = 0;
254 const char *kname = sdlFindKeyName(key->keysym.sym);
255 if (kname[0] && !kname[1] && kname[0] >= 'a' && kname[0] <= 'z') {
256 buf[bpos++] = kname[0]-32;
257 } else {
258 strcat(buf, kname);
259 bpos += strlen(kname);
261 buf[bpos] = 0;
262 return buf;
266 static SDLJimBinding *sdlFindBinding (SDLJimBinding *list, const SDLJimBinding *bind, SDLJimBinding **pprev) {
267 SDLJimBinding *prev = NULL;
269 if (pprev) *pprev = NULL;
270 for (SDLJimBinding *res = list; res != NULL; res = res->next) {
271 if (res->sdlk == bind->sdlk && res->modvalue == bind->modvalue) {
272 if (pprev) *pprev = prev;
273 return res;
275 prev = res;
277 return NULL;
281 SDLJimBinding *sdlFindKeyBind (SDLJimBinding *list, uint16_t sdlk, uint16_t modvalue) {
282 //fprintf(stderr, "====\n");
283 modvalue &= KMOD_CTRL|KMOD_SHIFT|KMOD_ALT|KMOD_META;
284 for (SDLJimBinding *res = list; res != NULL; res = res->next) {
285 //fprintf(stderr, "BIND:[0x%04x;0x%04x] == [0x%04x;0x%04x]\n", res->sdlk, res->modvalue, sdlk, modvalue);
286 if (res->sdlk == sdlk) {
287 uint16_t mv = modvalue;
289 if ((res->modvalue&KMOD_CTRL) == KMOD_CTRL && (mv&KMOD_CTRL)) mv |= KMOD_CTRL;
290 if ((res->modvalue&KMOD_SHIFT) == KMOD_SHIFT && (mv&KMOD_SHIFT)) mv |= KMOD_SHIFT;
291 if ((res->modvalue&KMOD_ALT) == KMOD_ALT && (mv&KMOD_ALT)) mv |= KMOD_ALT;
292 if ((res->modvalue&KMOD_META) == KMOD_META && (mv&KMOD_META)) mv |= KMOD_META;
293 if (res->modvalue == mv) return res;
296 return NULL;
300 static void sdlClearBindings (Jim_Interp *interp, SDLJimBinding *list) {
301 while (list != NULL) {
302 SDLJimBinding *bind = list;
304 list = list->next;
305 if (bind->action != NULL) Jim_DecrRefCount(interp, bind->action);
306 free(bind);
311 // cmdname mods... key action
312 static SDLJimBinding *sdlParseBinding (int argc, Jim_Obj *const *argv, int hasaction) {
313 uint16_t sdlk = 0;
314 uint16_t modvalue = 0;
315 SDLJimBinding *bind;
317 //memset(&bind, 0, sizeof(bind));
318 if (argc < 3) return NULL;
319 for (int f = 1; f < argc-hasaction; ++f) {
320 const char *a0 = Jim_String(argv[f]);
321 //fprintf(stderr, "sdlParseBinding: f=%d; [%s]\n", f, a0);
322 if (strcasecmp(a0, "ctrl") == 0) modvalue |= KMOD_CTRL;
323 else if (strcasecmp(a0, "lctrl") == 0) modvalue |= KMOD_LCTRL;
324 else if (strcasecmp(a0, "rctrl") == 0) modvalue |= KMOD_RCTRL;
325 else if (strcasecmp(a0, "alt") == 0) modvalue |= KMOD_ALT;
326 else if (strcasecmp(a0, "lalt") == 0) modvalue |= KMOD_LALT;
327 else if (strcasecmp(a0, "ralt") == 0) modvalue |= KMOD_RALT;
328 else if (strcasecmp(a0, "shift") == 0) modvalue |= KMOD_SHIFT;
329 else if (strcasecmp(a0, "lshift") == 0) modvalue |= KMOD_LSHIFT;
330 else if (strcasecmp(a0, "rshift") == 0) modvalue |= KMOD_RSHIFT;
331 else if (strcasecmp(a0, "meta") == 0) modvalue |= KMOD_META;
332 else {
333 if (sdlk != 0) {
334 cprintf("%s: double key name ('%s')\n", Jim_String(argv[0]), a0);
335 return NULL;
337 if ((sdlk = sdlFindKeyCode(a0)) == 0) {
338 cprintf("%s: unknown key name ('%s')\n", Jim_String(argv[0]), a0);
339 return NULL;
344 if (sdlk == 0) {
345 cprintf("%s: no key name\n", Jim_String(argv[0]));
346 return NULL;
349 if ((bind = malloc(sizeof(*bind))) != NULL) {
350 bind->sdlk = sdlk;
351 bind->modvalue = modvalue;
352 if (hasaction) {
353 bind->action = Jim_DuplicateObj(jim, argv[argc-1]);
354 Jim_IncrRefCount(bind->action);
355 } else {
356 bind->action = NULL;
358 bind->next = NULL;
361 return bind;
365 ////////////////////////////////////////////////////////////////////////////////
366 // zxbind clear
367 // zxbind sdlkey zxkey [zxkey]
368 JIMAPI_FN(zxbind) {
369 uint16_t sdlk;
370 const char *a0, *a1, *a2;
372 if (jimapiDisableCompletion(interp, argc, argv)) return JIM_OK;
373 if (argc == 2) {
374 // this should be "zxbind clear"
375 if (strcasecmp("clear", Jim_String(argv[1])) == 0) {
376 memset(zxKeyBinds, 0, sizeof(zxKeyBinds));
377 Jim_SetResultBool(interp, 1);
378 return JIM_OK;
382 if (argc < 3 || argc > 4) { Jim_WrongNumArgs(interp, 1, argv, "string"); return JIM_ERR; }
383 a0 = Jim_String(argv[1]);
384 a1 = Jim_String(argv[2]);
385 a2 = (argc > 3 ? Jim_String(argv[3]) : NULL);
386 if ((sdlk = sdlFindKeyCode(a0)) == 0) {
387 //cprintf("zxbind: unknown pc key: '%s'\n", a0);
388 jim_SetResStrf(interp, "%s: unknown pc key: '%s'", Jim_String(argv[0]), a0);
389 return JIM_ERR;
392 zxBindSDLK(sdlk, a1, a2);
393 Jim_SetResultBool(interp, 1);
394 return JIM_OK;
398 // zxunbind sdlkey
399 JIMAPI_FN(zxunbind) {
400 uint16_t sdlk;
401 const char *a0;
402 int l0;
403 if (jimapiDisableCompletion(interp, argc, argv)) return JIM_OK;
404 if (argc != 2) { Jim_WrongNumArgs(interp, 1, argv, "string"); return JIM_ERR; }
405 a0 = Jim_GetString(argv[1], &l0);
406 if ((sdlk = sdlFindKeyCode(a0)) == 0) {
407 jim_SetResStrf(interp, "%s: unknown pc key: '%s'", Jim_String(argv[0]), a0);
408 return JIM_ERR;
410 zxBindSDLK(sdlk, NULL, NULL);
411 Jim_SetResultBool(interp, 1);
412 return JIM_OK;
416 ////////////////////////////////////////////////////////////////////////////////
417 // bind clear
418 // bind sdlkey string
419 JIMAPI_FN(bind) {
420 SDLJimBinding *bind, *prev, *old;
422 if (jimapiDisableCompletion(interp, argc, argv)) return JIM_OK;
423 if (argc == 2) {
424 if (strcasecmp("clear", Jim_String(argv[1])) == 0) {
425 sdlClearBindings(interp, sdlJimBindings);
426 sdlJimBindings = NULL;
427 Jim_SetResultBool(interp, 1);
428 return JIM_OK;
432 if (argc < 3) { Jim_WrongNumArgs(interp, 1, argv, "string"); return JIM_ERR; }
433 if ((bind = sdlParseBinding(argc, argv, 1)) == NULL) {
434 jim_SetResStrf(interp, "%s: can't parse arguments", Jim_String(argv[0]));
435 return JIM_ERR;
438 // replace old binding if there is any
439 if ((old = sdlFindBinding(sdlJimBindings, bind, &prev)) != NULL) {
440 if (old->action != NULL) Jim_DecrRefCount(interp, old->action);
441 old->action = bind->action;
442 free(bind);
443 bind = old;
444 } else {
445 bind->next = sdlJimBindings;
446 sdlJimBindings = bind;
449 //Jim_IncrRefCount(bind->action);
450 Jim_SetResultBool(interp, 1);
451 return JIM_OK;
455 // unbind sdlkey
456 JIMAPI_FN(unbind) {
457 SDLJimBinding *bind, *prev, *old;
459 if (jimapiDisableCompletion(interp, argc, argv)) return JIM_OK;
460 if (argc < 2) { Jim_WrongNumArgs(interp, 1, argv, "string"); return JIM_ERR; }
461 if ((bind = sdlParseBinding(argc, argv, 0)) == NULL) {
462 jim_SetResStrf(interp, "%s: can't parse arguments", Jim_String(argv[0]));
463 return JIM_ERR;
466 // remove old binding if there is any
467 if ((old = sdlFindBinding(sdlJimBindings, bind, &prev)) != NULL) {
468 free(bind);
469 if (prev != NULL) prev->next = old->next; else sdlJimBindings = old->next;
470 if (old->action != NULL) Jim_DecrRefCount(interp, old->action);
471 free(old);
472 Jim_SetResultBool(interp, 1);
473 } else {
474 Jim_SetResultBool(interp, 0);
477 return JIM_OK;