1 /** \file kbd.c
2 * \brief Native GTK3 UI keyboard stuff
3 *
4 * \author Marco van den Heuvel <blackystardust68@yahoo.com>
5 * \author Michael C. Martin <mcmartin@gmail.com>
6 * \author Oliver Schaertel
7 * \author pottendo <pottendo@gmx.net>
8 * \author Bas Wassink <b.wassink@ziggo.nl>
9 */
10
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
34 #include <stdio.h>
35 #include <gtk/gtk.h>
36 #include "debug_gtk3.h"
37 #include "lib.h"
38 #include "log.h"
39 #include "ui.h"
40
41 /* UNIX-specific; for kbd_arch_get_host_mapping */
42 #include <locale.h>
43 #include <string.h>
44
45
46 #include "keyboard.h"
47 #include "kbd.h"
48
49
50 static gboolean kbd_hotkey_handle(GdkEvent *report);
51
52
53 /** \brief Initial size of the hotkeys array
54 */
55 #define HOTKEYS_SIZE_INIT 64
56
57
58 /** \brief List of custom hotkeys
59 */
60 static kbd_gtk3_hotkey_t *hotkeys_list = NULL;
61
62
63 /** \brief Size of the hotkeys array
64 *
65 * This will be HOTKEYS_SIZE_INIT element after initializing and will grow
66 * by doubling its size when the array is full.
67 */
68 static int hotkeys_size = 0;
69
70
71 /** \brief Number of registered hotkeys
72 */
73 static int hotkeys_count = 0;
74
75
76
77
kbd_arch_get_host_mapping(void)78 int kbd_arch_get_host_mapping(void)
79 {
80 int n;
81 char *l;
82 int maps[KBD_MAPPING_NUM] = {
83 KBD_MAPPING_US, KBD_MAPPING_UK, KBD_MAPPING_DE, KBD_MAPPING_DA,
84 KBD_MAPPING_NO, KBD_MAPPING_FI, KBD_MAPPING_IT };
85 /* TODO: This is a UNIX-specific version lifted from the SDL
86 * implementation. */
87 char str[KBD_MAPPING_NUM][6] = {
88 "en_US", "en_UK", "de", "da", "no", "fi", "it"};
89 setlocale(LC_ALL, "");
90 l = setlocale(LC_ALL, NULL);
91 if (l && (strlen(l) > 1)) {
92 for (n = 1; n < KBD_MAPPING_NUM; n++) {
93 if (strncmp(l, str[n], strlen(str[n])) == 0) {
94 return maps[n];
95 }
96 }
97 }
98 return KBD_MAPPING_US;
99 }
100
101
102 /** \brief Initialize keyboard handling
103 */
kbd_arch_init(void)104 void kbd_arch_init(void)
105 {
106 /* do NOT call kbd_hotkey_init(), keyboard.c calls this function *after*
107 * the UI init stuff is called, allocating the hotkeys array again and thus
108 * causing a memory leak
109 */
110 }
111
112
113
kbd_arch_shutdown(void)114 void kbd_arch_shutdown(void)
115 {
116 /* Also don't call kbd_hotkey_shutdown() here */
117 }
118
119
kbd_arch_keyname_to_keynum(char * keyname)120 signed long kbd_arch_keyname_to_keynum(char *keyname)
121 {
122 guint sym = gdk_keyval_from_name(keyname);
123
124 if (sym == GDK_KEY_VoidSymbol) {
125 return -1;
126 }
127
128 return (signed long)sym;
129 }
130
kbd_arch_keynum_to_keyname(signed long keynum)131 const char *kbd_arch_keynum_to_keyname(signed long keynum)
132 {
133 return gdk_keyval_name((guint)keynum);
134 }
135
kbd_initialize_numpad_joykeys(int * joykeys)136 void kbd_initialize_numpad_joykeys(int *joykeys)
137 {
138 joykeys[0] = GDK_KEY_KP_0;
139 joykeys[1] = GDK_KEY_KP_1;
140 joykeys[2] = GDK_KEY_KP_2;
141 joykeys[3] = GDK_KEY_KP_3;
142 joykeys[4] = GDK_KEY_KP_4;
143 joykeys[5] = GDK_KEY_KP_6;
144 joykeys[6] = GDK_KEY_KP_7;
145 joykeys[7] = GDK_KEY_KP_8;
146 joykeys[8] = GDK_KEY_KP_9;
147 }
148
kbd_event_handler(GtkWidget * w,GdkEvent * report,gpointer gp)149 static gboolean kbd_event_handler(GtkWidget *w, GdkEvent *report, gpointer gp)
150 {
151 gint key;
152
153 key = report->key.keyval;
154 switch (report->type) {
155 case GDK_KEY_PRESS:
156 /* fprintf(stderr, "KeyPress: %d.\n", key); */
157 if (gtk_window_activate_key(GTK_WINDOW(w), (GdkEventKey *)report)) {
158 return TRUE;
159 }
160 /* For some reason, the Alt-D of going fullscreen doesn't
161 * return true when CAPS LOCK isn't on, but only it does
162 * this. */
163 if (key == GDK_KEY_d && report->key.state & GDK_MOD1_MASK) {
164 return TRUE;
165 }
166
167 /* check the custom hotkeys */
168 if (kbd_hotkey_handle(report)) {
169 return TRUE;
170 }
171
172 #if 0
173 if ((key == GDK_KEY_p || key == GDK_KEY_P)
174 && (report->key.state & GDK_MOD1_MASK)) {
175 debug_gtk3("Got Alt+P");
176 ui_toggle_pause();
177 return TRUE;
178 }
179 #endif
180
181 keyboard_key_pressed((signed long)key);
182 return TRUE;
183 case GDK_KEY_RELEASE:
184 /* fprintf(stderr, "KeyRelease: %d.\n", key); */
185 if (key == GDK_KEY_Shift_L || key == GDK_KEY_Shift_R ||
186 key == GDK_KEY_ISO_Level3_Shift) {
187 keyboard_key_clear();
188 }
189 keyboard_key_released(key);
190 break;
191 case GDK_ENTER_NOTIFY:
192 case GDK_LEAVE_NOTIFY:
193 case GDK_FOCUS_CHANGE:
194 keyboard_key_clear();
195 break;
196 default:
197 break;
198 } /* switch */
199 return FALSE;
200 }
201
kbd_connect_handlers(GtkWidget * widget,void * data)202 void kbd_connect_handlers(GtkWidget *widget, void *data)
203 {
204 g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(kbd_event_handler), data);
205 g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(kbd_event_handler), data);
206 g_signal_connect(G_OBJECT(widget), "enter-notify-event", G_CALLBACK(kbd_event_handler), data);
207 g_signal_connect(G_OBJECT(widget), "leave-notify-event", G_CALLBACK(kbd_event_handler), data);
208 }
209
210 /*
211 * Hotkeys (keyboard shortcuts not connected to any GtkMenuItem) handling
212 */
213
214
215 /** \brief Initialize the hotkeys
216 *
217 * This allocates an initial hotkeys array of HOTKEYS_SIZE_INIT elements
218 */
kbd_hotkey_init(void)219 void kbd_hotkey_init(void)
220 {
221 debug_gtk3("initializing hotkeys list.");
222 hotkeys_list = lib_malloc(HOTKEYS_SIZE_INIT * sizeof *hotkeys_list);
223 hotkeys_size = HOTKEYS_SIZE_INIT;
224 hotkeys_count = 0;
225 }
226
227
228
229 /** \brief Clean up memory used by the hotkeys array
230 */
kbd_hotkey_shutdown(void)231 void kbd_hotkey_shutdown(void)
232 {
233 debug_gtk3("cleaning up memory used by the hotkeys.");
234 lib_free(hotkeys_list);
235 }
236
237
238 /** \brief Find hotkey index
239 *
240 * \param[in] code key code
241 * \param[in] mask key mask
242 *
243 * \return index in list, -1 when not found
244 */
kbd_hotkey_get_index(guint code,guint mask)245 static int kbd_hotkey_get_index(guint code, guint mask)
246 {
247 int i = 0;
248
249 while (i < hotkeys_count) {
250 if (hotkeys_list[i].code == code && hotkeys_list[i].mask) {
251 return i;
252 }
253 i++;
254 }
255 return -1;
256 }
257
258
259 /** \brief Look up the requested hotkey and trigger its callback when found
260 *
261 * \param[in] report GDK key press event instance
262 *
263 * \return TRUE when the key was found and the callback triggered,
264 * FALSE otherwise
265 */
kbd_hotkey_handle(GdkEvent * report)266 static gboolean kbd_hotkey_handle(GdkEvent *report)
267 {
268 int i = 0;
269 gint code = report->key.keyval;
270
271 while (i < hotkeys_count) {
272 if ((hotkeys_list[i].code == code)
273 && (report->key.state & hotkeys_list[i].mask)) {
274
275 debug_gtk3("triggering callback of hotkey with index %d.", i);
276 hotkeys_list[i].callback();
277 return TRUE;
278 }
279 i++;
280 }
281 return FALSE;
282 }
283
284
285 /** \brief Add hotkey to the list
286 *
287 * \param[in] code GDK key code
288 * \param[in] mask GDK key modifier bitmask
289 * \param[in] callback function to call when hotkey is triggered
290 *
291 * \return bool
292 */
kbd_hotkey_add(guint code,guint mask,void (* callback)(void))293 gboolean kbd_hotkey_add(guint code, guint mask, void (*callback)(void))
294 {
295 if (callback == NULL) {
296 log_error(LOG_ERR, "Error: NULL passed as callback.");
297 return FALSE;
298 }
299 if (kbd_hotkey_get_index(code, mask) >= 0) {
300 log_error(LOG_ERR, "Error: hotkey already registered.");
301 return FALSE;
302 }
303
304 /* resize list? */
305 if (hotkeys_count == hotkeys_size) {
306 int new_size = hotkeys_size * 2;
307 debug_gtk3("Resizing hotkeys list to %d items.", new_size);
308 hotkeys_list = lib_realloc(
309 hotkeys_list,
310 (size_t)new_size * sizeof *hotkeys_list);
311 hotkeys_size = new_size;
312 }
313
314
315 /* register hotkey */
316 hotkeys_list[hotkeys_count].code = code;
317 hotkeys_list[hotkeys_count].mask = mask;
318 hotkeys_list[hotkeys_count].callback = callback;
319 hotkeys_count++;
320 return TRUE;
321 }
322
323
324 /** \brief Add multiple hotkeys at once
325 *
326 * Adds multiple hotkeys from \a list. Terminate the list with NULL for the
327 * callback value.
328 *
329 * \param[in] list list of hotkeys
330 *
331 * \return TRUE on success, FALSE if the list was exhausted or a hotkey
332 * was already registered
333 */
kbd_hotkey_add_list(kbd_gtk3_hotkey_t * list)334 gboolean kbd_hotkey_add_list(kbd_gtk3_hotkey_t *list)
335 {
336 int i = 0;
337
338 while (list[i].callback != NULL) {
339 if (!kbd_hotkey_add(list[i].code, list[i].mask, list[i].callback)) {
340 return FALSE;
341 }
342 i++;
343 }
344 return TRUE;
345 }
346