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