1 /*
2 * kbd.c - SDL keyboard driver.
3 *
4 * Written by
5 * Hannu Nuotio <hannu.nuotio@tut.fi>
6 * Marco van den Heuvel <blackystardust68@yahoo.com>
7 *
8 * Based on code by
9 * Ettore Perazzoli <ettore@comm2000.it>
10 * Andreas Boose <viceteam@t-online.de>
11 *
12 * This file is part of VICE, the Versatile Commodore Emulator.
13 * See README for copyright notice.
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
28 * 02111-1307 USA.
29 *
30 */
31
32 #include "vice.h"
33 #include "types.h"
34
35 #include "vice_sdl.h"
36 #include <stdio.h>
37 #include <string.h>
38
39 #include "archdep.h"
40 #include "cmdline.h"
41 #include "kbd.h"
42 #include "fullscreenarch.h"
43 #include "keyboard.h"
44 #include "lib.h"
45 #include "log.h"
46 #include "machine.h"
47 #include "monitor.h"
48 #include "resources.h"
49 #include "sysfile.h"
50 #include "ui.h"
51 #include "uihotkey.h"
52 #include "uimenu.h"
53 #include "util.h"
54 #include "vkbd.h"
55
56 /* #define SDL_DEBUG */
57
58 static log_t sdlkbd_log = LOG_ERR;
59
60 /* Hotkey filename */
61 static char *hotkey_file = NULL;
62
63 /* Menu keys */
64 int sdl_ui_menukeys[MENU_ACTION_NUM];
65
66 /* UI hotkeys: index is the key(combo), value is a pointer to the menu item.
67 4 is the number of the supported modifiers: shift, alt, control, meta. */
68 #define SDLKBD_UI_HOTKEYS_MAX (SDL_NUM_SCANCODES * (1 << 4))
69 ui_menu_entry_t *sdlkbd_ui_hotkeys[SDLKBD_UI_HOTKEYS_MAX];
70
71 /* ------------------------------------------------------------------------ */
72
73 /* Resources. */
74
hotkey_file_set(const char * val,void * param)75 static int hotkey_file_set(const char *val, void *param)
76 {
77 #ifdef SDL_DEBUG
78 fprintf(stderr, "%s: %s\n", __func__, val);
79 #endif
80
81 if (util_string_set(&hotkey_file, val)) {
82 return 0;
83 }
84
85 return sdlkbd_hotkeys_load(hotkey_file);
86 }
87
88 static resource_string_t resources_string[] = {
89 { "HotkeyFile", NULL, RES_EVENT_NO, NULL,
90 &hotkey_file, hotkey_file_set, (void *)0 },
91 RESOURCE_STRING_LIST_END
92 };
93
sdlkbd_init_resources(void)94 int sdlkbd_init_resources(void)
95 {
96 resources_string[0].factory_value = archdep_default_hotkey_file_name();
97
98 if (resources_register_string(resources_string) < 0) {
99 return -1;
100 }
101 return 0;
102 }
103
sdlkbd_resources_shutdown(void)104 void sdlkbd_resources_shutdown(void)
105 {
106 lib_free(resources_string[0].factory_value);
107 resources_string[0].factory_value = NULL;
108 lib_free(hotkey_file);
109 hotkey_file = NULL;
110 }
111
112 /* ------------------------------------------------------------------------ */
113
114 static const cmdline_option_t cmdline_options[] =
115 {
116 { "-hotkeyfile", SET_RESOURCE, CMDLINE_ATTRIB_NEED_ARGS,
117 NULL, NULL, "HotkeyFile", NULL,
118 "<name>", "Specify name of hotkey file" },
119 CMDLINE_LIST_END
120 };
121
sdlkbd_init_cmdline(void)122 int sdlkbd_init_cmdline(void)
123 {
124 return cmdline_register_options(cmdline_options);
125 }
126
127 /* ------------------------------------------------------------------------ */
128
129 /* Convert 'known' keycodes to SDL1x keycodes.
130 Unicode keycodes and 'unknown' keycodes are
131 translated to 'SDLK_UNKNOWN'.
132
133 This makes SDL2 key handling compatible with
134 SDL1 key handling, but a proper solution for
135 handling unicode will still need to be made.
136 */
137 #ifdef USE_SDLUI2
138 typedef struct SDL2Key_s {
139 SDLKey SDL1x;
140 SDLKey SDL2x;
141 } SDL2Key_t;
142
143 static SDL2Key_t SDL2xKeys[] = {
144 { 12, SDLK_CLEAR },
145 { 19, SDLK_PAUSE },
146 { 256, SDLK_KP_0 },
147 { 257, SDLK_KP_1 },
148 { 258, SDLK_KP_2 },
149 { 259, SDLK_KP_3 },
150 { 260, SDLK_KP_4 },
151 { 261, SDLK_KP_5 },
152 { 262, SDLK_KP_6 },
153 { 263, SDLK_KP_7 },
154 { 264, SDLK_KP_8 },
155 { 265, SDLK_KP_9 },
156 { 266, SDLK_KP_PERIOD },
157 { 267, SDLK_KP_DIVIDE },
158 { 268, SDLK_KP_MULTIPLY },
159 { 269, SDLK_KP_MINUS },
160 { 270, SDLK_KP_PLUS },
161 { 271, SDLK_KP_ENTER },
162 { 272, SDLK_KP_EQUALS },
163 { 273, SDLK_UP },
164 { 274, SDLK_DOWN },
165 { 275, SDLK_RIGHT },
166 { 276, SDLK_LEFT },
167 { 277, SDLK_INSERT },
168 { 278, SDLK_HOME },
169 { 279, SDLK_END },
170 { 280, SDLK_PAGEUP },
171 { 281, SDLK_PAGEDOWN },
172 { 282, SDLK_F1 },
173 { 283, SDLK_F2 },
174 { 284, SDLK_F3 },
175 { 285, SDLK_F4 },
176 { 286, SDLK_F5 },
177 { 287, SDLK_F6 },
178 { 288, SDLK_F7 },
179 { 289, SDLK_F8 },
180 { 290, SDLK_F9 },
181 { 291, SDLK_F10 },
182 { 292, SDLK_F11 },
183 { 293, SDLK_F12 },
184 { 294, SDLK_F13 },
185 { 295, SDLK_F14 },
186 { 296, SDLK_F15 },
187 { 300, SDLK_NUMLOCKCLEAR },
188 { 301, SDLK_CAPSLOCK },
189 { 302, SDLK_SCROLLLOCK },
190 { 303, SDLK_RSHIFT },
191 { 304, SDLK_LSHIFT },
192 { 305, SDLK_RCTRL },
193 { 306, SDLK_LCTRL },
194 { 307, SDLK_RALT },
195 { 308, SDLK_LALT },
196 { 309, SDLK_RGUI },
197 { 310, SDLK_LGUI },
198 { 313, SDLK_MODE },
199 { 314, SDLK_APPLICATION },
200 { 315, SDLK_HELP },
201 { 316, SDLK_PRINTSCREEN },
202 { 317, SDLK_SYSREQ },
203 { 319, SDLK_MENU },
204 { 320, SDLK_POWER },
205 { 322, SDLK_UNDO },
206 { 0, SDLK_UNKNOWN }
207 };
208
SDL2x_to_SDL1x_Keys(SDLKey key)209 SDLKey SDL2x_to_SDL1x_Keys(SDLKey key)
210 {
211 int i;
212
213 /* keys 0-255 are the same on SDL1x */
214 if (key < 256) {
215 return key;
216 }
217
218 /* keys 0x40000xxx need translation */
219 if (key & 0x40000000) {
220 for (i = 0; SDL2xKeys[i].SDL1x; ++i) {
221 if (SDL2xKeys[i].SDL2x == key) {
222 return SDL2xKeys[i].SDL1x;
223 }
224 } /* fallthrough, unknown SDL2x key */
225 } else { /* SDL1 format key may come from ini file */
226 for (i = 0; SDL2xKeys[i].SDL1x; ++i) {
227 if (SDL2xKeys[i].SDL1x == key) {
228 return SDL2xKeys[i].SDL1x;
229 }
230 }
231 }
232
233 /* unicode key, so return 'unknown' */
234 return SDLK_UNKNOWN;
235 }
236
SDL1x_to_SDL2x_Keys(SDLKey key)237 SDLKey SDL1x_to_SDL2x_Keys(SDLKey key)
238 {
239 int i;
240
241 for (i = 0; SDL2xKeys[i].SDL1x; ++i) {
242 if (SDL2xKeys[i].SDL1x == key) {
243 return SDL2xKeys[i].SDL2x;
244 }
245 }
246
247 return key;
248 }
249 #else
SDL2x_to_SDL1x_Keys(SDLKey key)250 SDLKey SDL2x_to_SDL1x_Keys(SDLKey key)
251 {
252 return key;
253 }
254
SDL1x_to_SDL2x_Keys(SDLKey key)255 SDLKey SDL1x_to_SDL2x_Keys(SDLKey key)
256 {
257 return key;
258 }
259 #endif
260
sdlkbd_key_mod_to_index(SDLKey key,SDLMod mod)261 static inline int sdlkbd_key_mod_to_index(SDLKey key, SDLMod mod)
262 {
263 int i = 0;
264
265 mod &= (KMOD_CTRL | KMOD_SHIFT | KMOD_ALT | KMOD_META);
266
267 if (mod) {
268 if (mod & KMOD_SHIFT) {
269 i |= (1 << 0);
270 }
271
272 if (mod & KMOD_ALT) {
273 i |= (1 << 1);
274 }
275
276 if (mod & KMOD_CTRL) {
277 i |= (1 << 2);
278 }
279
280 if (mod & KMOD_META) {
281 i |= (1 << 3);
282 }
283 }
284 return (i * SDL_NUM_SCANCODES) + key;
285 }
286
sdlkbd_get_hotkey(SDLKey key,SDLMod mod)287 static ui_menu_entry_t *sdlkbd_get_hotkey(SDLKey key, SDLMod mod)
288 {
289 return sdlkbd_ui_hotkeys[sdlkbd_key_mod_to_index(key, mod)];
290 }
291
sdlkbd_set_hotkey(SDLKey key,SDLMod mod,ui_menu_entry_t * value)292 void sdlkbd_set_hotkey(SDLKey key, SDLMod mod, ui_menu_entry_t *value)
293 {
294 sdlkbd_ui_hotkeys[sdlkbd_key_mod_to_index(key, mod)] = value;
295 }
296
sdlkbd_keyword_clear(void)297 static void sdlkbd_keyword_clear(void)
298 {
299 int i;
300
301 for (i = 0; i < SDLKBD_UI_HOTKEYS_MAX; ++i) {
302 sdlkbd_ui_hotkeys[i] = NULL;
303 }
304 }
305
sdlkbd_parse_keyword(char * buffer)306 static void sdlkbd_parse_keyword(char *buffer)
307 {
308 char *key;
309
310 key = strtok(buffer + 1, " \t:");
311
312 if (!strcmp(key, "CLEAR")) {
313 sdlkbd_keyword_clear();
314 }
315 }
316
sdlkbd_parse_entry(char * buffer)317 static void sdlkbd_parse_entry(char *buffer)
318 {
319 char *p;
320 char *full_path;
321 int keynum;
322 ui_menu_entry_t *action;
323
324 p = strtok(buffer, " \t:");
325
326 keynum = atoi(p);
327
328 if (keynum >= SDLKBD_UI_HOTKEYS_MAX) {
329 log_error(sdlkbd_log, "Too large hotkey %i!", keynum);
330 return;
331 }
332
333 p = strtok(NULL, "\r\n");
334 if (p != NULL) {
335 full_path = lib_stralloc(p);
336 action = sdl_ui_hotkey_action(p);
337 if (action == NULL) {
338 log_warning(sdlkbd_log, "Cannot find menu item \"%s\"!", full_path);
339 } else {
340 sdlkbd_ui_hotkeys[keynum] = action;
341 }
342 lib_free(full_path);
343 }
344 }
345
sdlkbd_hotkeys_load(const char * filename)346 int sdlkbd_hotkeys_load(const char *filename)
347 {
348 FILE *fp;
349 char *complete_path = NULL;
350 char buffer[1000];
351
352 /* Silently ignore keymap load on resource & cmdline init */
353 if (sdlkbd_log == LOG_ERR) {
354 return 0;
355 }
356
357 if (filename == NULL) {
358 log_warning(sdlkbd_log, "Failed to open NULL.");
359 return -1;
360 }
361
362 fp = sysfile_open(filename, &complete_path, MODE_READ_TEXT);
363
364 if (fp == NULL) {
365 log_warning(sdlkbd_log, "Failed to open `%s'.", filename);
366 return -1;
367 }
368
369 log_message(sdlkbd_log, "Loading hotkey map `%s'.", complete_path);
370
371 lib_free(complete_path);
372
373 do {
374 buffer[0] = 0;
375 if (fgets(buffer, 999, fp)) {
376
377 if (strlen(buffer) == 0) {
378 break;
379 }
380 buffer[strlen(buffer) - 1] = 0; /* remove newline */
381
382 /* remove comments */
383 if (buffer[0] == '#') {
384 buffer[0] = 0;
385 }
386
387 switch (*buffer) {
388 case 0:
389 break;
390 case '!':
391 /* keyword handling */
392 sdlkbd_parse_keyword(buffer);
393 break;
394 default:
395 /* table entry handling */
396 sdlkbd_parse_entry(buffer);
397 break;
398 }
399 }
400 } while (!feof(fp));
401 fclose(fp);
402
403 return 0;
404 }
405
sdlkbd_hotkeys_dump(const char * filename)406 int sdlkbd_hotkeys_dump(const char *filename)
407 {
408 FILE *fp;
409 int i;
410 char *hotkey_path;
411
412 if (filename == NULL) {
413 return -1;
414 }
415
416 fp = fopen(filename, MODE_WRITE_TEXT);
417
418 if (fp == NULL) {
419 return -1;
420 }
421
422 fprintf(fp, "# VICE hotkey mapping file\n"
423 "#\n"
424 "# A hotkey map is read in as a patch to the current map.\n"
425 "#\n"
426 "# File format:\n"
427 "# - comment lines start with '#'\n"
428 "# - keyword lines start with '!keyword'\n"
429 "# - normal line has 'keynum path&to&menuitem'\n"
430 "#\n"
431 "# Keywords and their lines are:\n"
432 "# '!CLEAR' clear all mappings\n"
433 "#\n\n"
434 );
435
436 fprintf(fp, "!CLEAR\n\n");
437
438 for (i = 0; i < SDLKBD_UI_HOTKEYS_MAX; ++i) {
439 if (sdlkbd_ui_hotkeys[i]) {
440 hotkey_path = sdl_ui_hotkey_path(sdlkbd_ui_hotkeys[i]);
441 fprintf(fp, "%i %s\n", i, hotkey_path);
442 lib_free(hotkey_path);
443 }
444 }
445
446 fclose(fp);
447
448 return 0;
449 }
450
451 /* ------------------------------------------------------------------------ */
452
sdlkbd_press(SDLKey key,SDLMod mod)453 ui_menu_action_t sdlkbd_press(SDLKey key, SDLMod mod)
454 {
455 ui_menu_action_t i, retval = MENU_ACTION_NONE;
456 ui_menu_entry_t *hotkey_action = NULL;
457
458 #ifdef SDL_DEBUG
459 fprintf(stderr, "%s: %i (%s),%i\n", __func__, key, SDL_GetKeyName(key), mod);
460 #endif
461 if (sdl_menu_state || (sdl_vkbd_state & SDL_VKBD_ACTIVE)) {
462 if (key != SDLK_UNKNOWN) {
463 for (i = MENU_ACTION_UP; i < MENU_ACTION_NUM; ++i) {
464 if (sdl_ui_menukeys[i] == (int)key) {
465 retval = i;
466 break;
467 }
468 }
469 if ((int)(key) == sdl_ui_menukeys[0]) {
470 retval = MENU_ACTION_EXIT;
471 }
472 }
473 return retval;
474 }
475
476 if ((int)(key) == sdl_ui_menukeys[0]) {
477 sdl_ui_activate();
478 return retval;
479 }
480
481 if ((hotkey_action = sdlkbd_get_hotkey(key, mod)) != NULL) {
482 sdl_ui_hotkey(hotkey_action);
483 return retval;
484 }
485
486 keyboard_key_pressed((unsigned long)key);
487 return retval;
488 }
489
sdlkbd_release(SDLKey key,SDLMod mod)490 ui_menu_action_t sdlkbd_release(SDLKey key, SDLMod mod)
491 {
492 ui_menu_action_t i, retval = MENU_ACTION_NONE_RELEASE;
493
494 #ifdef SDL_DEBUG
495 fprintf(stderr, "%s: %i (%s),%i\n", __func__, key, SDL_GetKeyName(key), mod);
496 #endif
497 if (sdl_vkbd_state & SDL_VKBD_ACTIVE) {
498 if (key != SDLK_UNKNOWN) {
499 for (i = MENU_ACTION_UP; i < MENU_ACTION_NUM; ++i) {
500 if (sdl_ui_menukeys[i] == (int)key) {
501 retval = i;
502 break;
503 }
504 }
505 }
506 return retval + MENU_ACTION_NONE_RELEASE;
507 }
508
509 keyboard_key_released((unsigned long)key);
510 return retval;
511 }
512
513 /* ------------------------------------------------------------------------ */
514
kbd_arch_init(void)515 void kbd_arch_init(void)
516 {
517 #ifdef SDL_DEBUG
518 fprintf(stderr, "%s: hotkey table size %u (%lu bytes)\n", __func__, SDLKBD_UI_HOTKEYS_MAX, SDLKBD_UI_HOTKEYS_MAX * sizeof(ui_menu_entry_t *));
519 #endif
520
521 sdlkbd_log = log_open("SDLKeyboard");
522
523 sdlkbd_keyword_clear();
524 /* first load the defaults, then patch them with the user defined hotkeys */
525 if (machine_class == VICE_MACHINE_VSID) {
526 sdlkbd_hotkeys_load("sdl_hotkeys_vsid.vkm");
527 } else {
528 sdlkbd_hotkeys_load("sdl_hotkeys.vkm");
529 }
530 sdlkbd_hotkeys_load(hotkey_file);
531 }
532
kbd_arch_keyname_to_keynum(char * keyname)533 signed long kbd_arch_keyname_to_keynum(char *keyname)
534 {
535 signed long keynum = (signed long)atoi(keyname);
536 if (keynum == 0) {
537 log_warning(sdlkbd_log, "Keycode 0 is reserved for unknown keys.");
538 }
539 return keynum;
540 }
541
kbd_arch_keynum_to_keyname(signed long keynum)542 const char *kbd_arch_keynum_to_keyname(signed long keynum)
543 {
544 static char keyname[20];
545
546 memset(keyname, 0, 20);
547
548 sprintf(keyname, "%li", keynum);
549
550 return keyname;
551 }
552
kbd_initialize_numpad_joykeys(int * joykeys)553 void kbd_initialize_numpad_joykeys(int* joykeys)
554 {
555 joykeys[0] = SDL2x_to_SDL1x_Keys(SDLK_KP0);
556 joykeys[1] = SDL2x_to_SDL1x_Keys(SDLK_KP1);
557 joykeys[2] = SDL2x_to_SDL1x_Keys(SDLK_KP2);
558 joykeys[3] = SDL2x_to_SDL1x_Keys(SDLK_KP3);
559 joykeys[4] = SDL2x_to_SDL1x_Keys(SDLK_KP4);
560 joykeys[5] = SDL2x_to_SDL1x_Keys(SDLK_KP6);
561 joykeys[6] = SDL2x_to_SDL1x_Keys(SDLK_KP7);
562 joykeys[7] = SDL2x_to_SDL1x_Keys(SDLK_KP8);
563 joykeys[8] = SDL2x_to_SDL1x_Keys(SDLK_KP9);
564 }
565
kbd_get_menu_keyname(void)566 const char *kbd_get_menu_keyname(void)
567 {
568 return SDL_GetKeyName(SDL1x_to_SDL2x_Keys(sdl_ui_menukeys[0]));
569 }
570