comparison bindings.c @ 1648:b7ecd0d6a77b mame_interp

Merge from default
author Michael Pavone <pavone@retrodev.com>
date Tue, 25 Dec 2018 11:12:26 -0800
parents 6909c5d0bbb5
children 12d0c7c4ad80
comparison
equal deleted inserted replaced
1509:36732f5c2281 1648:b7ecd0d6a77b
1 #include <string.h>
2 #include "render.h"
3 #include "system.h"
4 #include "io.h"
5 #include "blastem.h"
6 #include "saves.h"
7 #include "util.h"
8 #include "genesis.h"
9 #include "sms.h"
10 #include "menu.h"
11 #include "bindings.h"
12 #include "controller_info.h"
13 #ifndef DISABLE_NUKLEAR
14 #include "nuklear_ui/blastem_nuklear.h"
15 #endif
16
17 enum {
18 BIND_NONE,
19 BIND_UI,
20 BIND_GAMEPAD,
21 BIND_MOUSE
22 };
23
24 typedef enum {
25 UI_DEBUG_MODE_INC,
26 UI_ENTER_DEBUGGER,
27 UI_SAVE_STATE,
28 UI_SET_SPEED,
29 UI_NEXT_SPEED,
30 UI_PREV_SPEED,
31 UI_RELEASE_MOUSE,
32 UI_TOGGLE_KEYBOARD_CAPTURE,
33 UI_TOGGLE_FULLSCREEN,
34 UI_SOFT_RESET,
35 UI_RELOAD,
36 UI_SMS_PAUSE,
37 UI_SCREENSHOT,
38 UI_EXIT,
39 UI_PLANE_DEBUG,
40 UI_VRAM_DEBUG,
41 UI_CRAM_DEBUG,
42 UI_COMPOSITE_DEBUG
43 } ui_action;
44
45 typedef struct {
46 uint8_t bind_type;
47 uint8_t subtype_a;
48 uint8_t subtype_b;
49 } keybinding;
50
51 typedef struct {
52 keybinding bindings[4];
53 uint8_t state;
54 } joydpad;
55
56 typedef struct {
57 keybinding positive;
58 keybinding negative;
59 int16_t value;
60 } joyaxis;
61
62 typedef struct {
63 keybinding *buttons;
64 joydpad *dpads;
65 joyaxis *axes;
66 uint32_t num_buttons; //number of entries in the buttons array, not necessarily the number of buttons on the device
67 uint32_t num_dpads; //number of entries in the dpads array, not necessarily the number of dpads on the device
68 uint32_t num_axes; //number of entries in the axes array, not necessarily the number of dpads on the device
69 } joystick;
70
71 typedef struct {
72 keybinding buttons[MAX_MOUSE_BUTTONS];
73 keybinding motion;
74 } mousebinding;
75
76 #define DEFAULT_JOYBUTTON_ALLOC 12
77 static keybinding *bindings[0x10000];
78 static joystick joysticks[MAX_JOYSTICKS];
79 static mousebinding mice[MAX_MICE];
80 const uint8_t dpadbits[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
81
82 static void do_bind(keybinding *binding, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
83 {
84 binding->bind_type = bind_type;
85 binding->subtype_a = subtype_a;
86 binding->subtype_b = subtype_b;
87 }
88
89 void bind_key(int keycode, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
90 {
91 int bucket = keycode >> 15 & 0xFFFF;
92 if (!bindings[bucket]) {
93 bindings[bucket] = malloc(sizeof(keybinding) * 0x8000);
94 memset(bindings[bucket], 0, sizeof(keybinding) * 0x8000);
95 }
96 int idx = keycode & 0x7FFF;
97 do_bind(bindings[bucket] + idx, bind_type, subtype_a, subtype_b);
98 }
99
100 void bind_button(int joystick, int button, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
101 {
102 if (joystick >= MAX_JOYSTICKS) {
103 return;
104 }
105 if (!joysticks[joystick].buttons) {
106 joysticks[joystick].num_buttons = button < DEFAULT_JOYBUTTON_ALLOC ? DEFAULT_JOYBUTTON_ALLOC : button + 1;
107 joysticks[joystick].buttons = calloc(joysticks[joystick].num_buttons, sizeof(keybinding));
108 } else if (joysticks[joystick].num_buttons <= button) {
109 uint32_t old_capacity = joysticks[joystick].num_buttons;
110 joysticks[joystick].num_buttons *= 2;
111 joysticks[joystick].buttons = realloc(joysticks[joystick].buttons, sizeof(keybinding) * joysticks[joystick].num_buttons);
112 memset(joysticks[joystick].buttons + old_capacity, 0, joysticks[joystick].num_buttons - old_capacity);
113 }
114 do_bind(joysticks[joystick].buttons + button, bind_type, subtype_a, subtype_b);
115 }
116
117 void bind_dpad(int joystick, int dpad, int direction, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
118 {
119 if (joystick >= MAX_JOYSTICKS) {
120 return;
121 }
122 if (!joysticks[joystick].dpads) {
123 //multiple D-pads/hats are not common, so don't allocate any extra space
124 joysticks[joystick].dpads = calloc(dpad+1, sizeof(joydpad));
125 joysticks[joystick].num_dpads = dpad+1;
126 } else if (joysticks[joystick].num_dpads <= dpad) {
127 uint32_t old_capacity = joysticks[joystick].num_dpads;
128 joysticks[joystick].num_dpads *= 2;
129 joysticks[joystick].dpads = realloc(joysticks[joystick].dpads, sizeof(joydpad) * joysticks[joystick].num_dpads);
130 memset(joysticks[joystick].dpads + old_capacity, 0, (joysticks[joystick].num_dpads - old_capacity) * sizeof(joydpad));
131 }
132 for (int i = 0; i < 4; i ++) {
133 if (dpadbits[i] & direction) {
134 do_bind(joysticks[joystick].dpads[dpad].bindings + i, bind_type, subtype_a, subtype_b);
135 break;
136 }
137 }
138 }
139
140 void bind_axis(int joystick, int axis, int positive, uint8_t bind_type, uint8_t subtype_a, uint8_t subtype_b)
141 {
142 if (joystick >= MAX_JOYSTICKS) {
143 return;
144 }
145 if (!joysticks[joystick].axes) {
146 //typical gamepad has 4 axes
147 joysticks[joystick].num_axes = axis+1 > 4 ? axis+1 : 4;
148 joysticks[joystick].axes = calloc(joysticks[joystick].num_axes, sizeof(joyaxis));
149 } else if (joysticks[joystick].num_axes <= axis) {
150 uint32_t old_capacity = joysticks[joystick].num_axes;
151 joysticks[joystick].num_axes *= 2;
152 joysticks[joystick].axes = realloc(joysticks[joystick].axes, sizeof(joyaxis) * joysticks[joystick].num_axes);
153 memset(joysticks[joystick].axes + old_capacity, 0, (joysticks[joystick].num_axes - old_capacity) * sizeof(joyaxis));
154 }
155 if (positive) {
156 do_bind(&joysticks[joystick].axes[axis].positive, bind_type, subtype_a, subtype_b);
157 } else {
158 do_bind(&joysticks[joystick].axes[axis].negative, bind_type, subtype_a, subtype_b);
159 }
160 }
161
162 void reset_joystick_bindings(int joystick)
163 {
164 if (joystick >= MAX_JOYSTICKS) {
165 return;
166 }
167 if (joysticks[joystick].buttons) {
168 for (int i = 0; i < joysticks[joystick].num_buttons; i++)
169 {
170 joysticks[joystick].buttons[i].bind_type = BIND_NONE;
171 }
172 }
173 if (joysticks[joystick].dpads) {
174 for (int i = 0; i < joysticks[joystick].num_dpads; i++)
175 {
176 for (int dir = 0; dir < 4; dir++)
177 {
178 joysticks[joystick].dpads[i].bindings[dir].bind_type = BIND_NONE;
179 }
180 }
181 }
182 if (joysticks[joystick].axes) {
183 for (int i = 0; i < joysticks[joystick].num_axes; i++)
184 {
185 joysticks[joystick].axes[i].positive.bind_type = BIND_NONE;
186 joysticks[joystick].axes[i].negative.bind_type = BIND_NONE;
187 }
188 }
189 }
190
191 void handle_binding_down(keybinding * binding)
192 {
193 if (!current_system) {
194 return;
195 }
196 if (binding->bind_type == BIND_GAMEPAD && current_system && current_system->gamepad_down)
197 {
198 current_system->gamepad_down(current_system, binding->subtype_a, binding->subtype_b);
199 }
200 else if (binding->bind_type == BIND_MOUSE && current_system && current_system->mouse_down)
201 {
202 current_system->mouse_down(current_system, binding->subtype_a, binding->subtype_b);
203 }
204 }
205
206 static uint8_t keyboard_captured;
207 void handle_keydown(int keycode, uint8_t scancode)
208 {
209 int bucket = keycode >> 15 & 0xFFFF;
210 int idx = keycode & 0x7FFF;
211 keybinding * binding = bindings[bucket] ? bindings[bucket] + idx : NULL;
212 if (binding && (!keyboard_captured || (binding->bind_type == BIND_UI && binding->subtype_a == UI_TOGGLE_KEYBOARD_CAPTURE))) {
213 handle_binding_down(binding);
214 } else if (keyboard_captured && current_system && current_system->keyboard_down) {
215 current_system->keyboard_down(current_system, scancode);
216 }
217 }
218
219 void handle_joydown(int joystick, int button)
220 {
221 if (joystick >= MAX_JOYSTICKS || button >= joysticks[joystick].num_buttons) {
222 return;
223 }
224 keybinding * binding = joysticks[joystick].buttons + button;
225 handle_binding_down(binding);
226 }
227
228 static uint8_t mouse_mode = MOUSE_NONE;
229 static uint8_t mouse_captured;
230 void handle_mousedown(int mouse, int button)
231 {
232 if (mouse_mode == MOUSE_CAPTURE && !mouse_captured) {
233 mouse_captured = 1;
234 render_relative_mouse(1);
235 return;
236 }
237 if (mouse >= MAX_MICE || button > MAX_MOUSE_BUTTONS || button <= 0) {
238 return;
239 }
240 keybinding * binding = mice[mouse].buttons + button - 1;
241 handle_binding_down(binding);
242 }
243
244 static int current_speed = 0;
245 static int num_speeds = 1;
246 static uint32_t * speeds = NULL;
247
248 static uint8_t mouse_captured;
249
250 #ifdef _WIN32
251 #define localtime_r(a,b) localtime(a)
252 #endif
253
254 void handle_binding_up(keybinding * binding)
255 {
256 switch(binding->bind_type)
257 {
258 case BIND_GAMEPAD:
259 if (current_system && current_system->gamepad_up) {
260 current_system->gamepad_up(current_system, binding->subtype_a, binding->subtype_b);
261 }
262 break;
263 case BIND_MOUSE:
264 if (current_system && current_system->mouse_up) {
265 current_system->mouse_up(current_system, binding->subtype_a, binding->subtype_b);
266 }
267 break;
268 case BIND_UI:
269 switch (binding->subtype_a)
270 {
271 case UI_DEBUG_MODE_INC:
272 current_system->inc_debug_mode(current_system);
273 break;
274 case UI_ENTER_DEBUGGER:
275 current_system->enter_debugger = 1;
276 break;
277 case UI_SAVE_STATE:
278 current_system->save_state = QUICK_SAVE_SLOT+1;
279 break;
280 case UI_NEXT_SPEED:
281 current_speed++;
282 if (current_speed >= num_speeds) {
283 current_speed = 0;
284 }
285 printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
286 current_system->set_speed_percent(current_system, speeds[current_speed]);
287 break;
288 case UI_PREV_SPEED:
289 current_speed--;
290 if (current_speed < 0) {
291 current_speed = num_speeds - 1;
292 }
293 printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
294 current_system->set_speed_percent(current_system, speeds[current_speed]);
295 break;
296 case UI_SET_SPEED:
297 if (binding->subtype_b < num_speeds) {
298 current_speed = binding->subtype_b;
299 printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]);
300 current_system->set_speed_percent(current_system, speeds[current_speed]);
301 } else {
302 printf("Setting speed to %d\n", speeds[current_speed]);
303 current_system->set_speed_percent(current_system, speeds[current_speed]);
304 }
305 break;
306 case UI_RELEASE_MOUSE:
307 if (mouse_captured) {
308 mouse_captured = 0;
309 render_relative_mouse(0);
310 }
311 break;
312 case UI_TOGGLE_KEYBOARD_CAPTURE:
313 if (current_system && current_system->has_keyboard) {
314 keyboard_captured = !keyboard_captured;
315 }
316 break;
317 case UI_TOGGLE_FULLSCREEN:
318 render_toggle_fullscreen();
319 break;
320 case UI_SOFT_RESET:
321 current_system->soft_reset(current_system);
322 break;
323 case UI_RELOAD:
324 reload_media();
325 break;
326 case UI_SMS_PAUSE:
327 if (current_system && current_system->gamepad_down) {
328 current_system->gamepad_down(current_system, GAMEPAD_MAIN_UNIT, MAIN_UNIT_PAUSE);
329 }
330 break;
331 case UI_SCREENSHOT: {
332 char *screenshot_base = tern_find_path(config, "ui\0screenshot_path\0", TVAL_PTR).ptrval;
333 if (!screenshot_base) {
334 screenshot_base = "$HOME";
335 }
336 tern_node *vars = tern_insert_ptr(NULL, "HOME", get_home_dir());
337 vars = tern_insert_ptr(vars, "EXEDIR", get_exe_dir());
338 screenshot_base = replace_vars(screenshot_base, vars, 1);
339 tern_free(vars);
340 time_t now = time(NULL);
341 struct tm local_store;
342 char fname_part[256];
343 char *template = tern_find_path(config, "ui\0screenshot_template\0", TVAL_PTR).ptrval;
344 if (!template) {
345 template = "blastem_%c.ppm";
346 }
347 strftime(fname_part, sizeof(fname_part), template, localtime_r(&now, &local_store));
348 char const *parts[] = {screenshot_base, PATH_SEP, fname_part};
349 char *path = alloc_concat_m(3, parts);
350 free(screenshot_base);
351 render_save_screenshot(path);
352 break;
353 }
354 case UI_EXIT:
355 #ifndef DISABLE_NUKLEAR
356 if (is_nuklear_active()) {
357 show_pause_menu();
358 } else {
359 #endif
360 current_system->request_exit(current_system);
361 if (current_system->type == SYSTEM_GENESIS) {
362 genesis_context *gen = (genesis_context *)current_system;
363 if (gen->extra) {
364 //TODO: More robust mechanism for detecting menu
365 menu_context *menu = gen->extra;
366 menu->external_game_load = 1;
367 }
368 }
369 #ifndef DISABLE_NUKLEAR
370 }
371 #endif
372 break;
373 case UI_PLANE_DEBUG:
374 case UI_VRAM_DEBUG:
375 case UI_CRAM_DEBUG:
376 case UI_COMPOSITE_DEBUG: {
377 vdp_context *vdp = NULL;
378 if (current_system->type == SYSTEM_GENESIS) {
379 genesis_context *gen = (genesis_context *)current_system;
380 vdp = gen->vdp;
381 } else if (current_system->type == SYSTEM_SMS) {
382 sms_context *sms = (sms_context *)current_system;
383 vdp = sms->vdp;
384 }
385 if (vdp) {
386 uint8_t debug_type;
387 switch(binding->subtype_a)
388 {
389 case UI_PLANE_DEBUG: debug_type = VDP_DEBUG_PLANE; break;
390 case UI_VRAM_DEBUG: debug_type = VDP_DEBUG_VRAM; break;
391 case UI_CRAM_DEBUG: debug_type = VDP_DEBUG_CRAM; break;
392 case UI_COMPOSITE_DEBUG: debug_type = VDP_DEBUG_COMPOSITE; break;
393 default: return;
394 }
395 vdp_toggle_debug_view(vdp, debug_type);
396 }
397 break;
398 }
399 }
400 break;
401 }
402 }
403
404 void handle_keyup(int keycode, uint8_t scancode)
405 {
406 int bucket = keycode >> 15 & 0xFFFF;
407 int idx = keycode & 0x7FFF;
408 keybinding * binding = bindings[bucket] ? bindings[bucket] + idx : NULL;
409 if (binding && (!keyboard_captured || (binding->bind_type == BIND_UI && binding->subtype_a == UI_TOGGLE_KEYBOARD_CAPTURE))) {
410 handle_binding_up(binding);
411 } else if (keyboard_captured && current_system && current_system->keyboard_up) {
412 current_system->keyboard_up(current_system, scancode);
413 }
414 }
415
416 void handle_joyup(int joystick, int button)
417 {
418 if (joystick >= MAX_JOYSTICKS || button >= joysticks[joystick].num_buttons) {
419 return;
420 }
421 keybinding * binding = joysticks[joystick].buttons + button;
422 handle_binding_up(binding);
423 }
424
425 void handle_joy_dpad(int joystick, int dpadnum, uint8_t value)
426 {
427 if (joystick >= MAX_JOYSTICKS || dpadnum >= joysticks[joystick].num_dpads) {
428 return;
429 }
430 joydpad * dpad = joysticks[joystick].dpads + dpadnum;
431 uint8_t newdown = (value ^ dpad->state) & value;
432 uint8_t newup = ((~value) ^ (~dpad->state)) & (~value);
433 dpad->state = value;
434 for (int i = 0; i < 4; i++) {
435 if (newdown & dpadbits[i]) {
436 handle_binding_down(dpad->bindings + i);
437 } else if(newup & dpadbits[i]) {
438 handle_binding_up(dpad->bindings + i);
439 }
440 }
441 }
442
443 #define JOY_AXIS_THRESHOLD 2000
444
445 void handle_joy_axis(int joystick, int axis, int16_t value)
446 {
447 if (joystick >= MAX_JOYSTICKS || axis >= joysticks[joystick].num_axes) {
448 return;
449 }
450 joyaxis *jaxis = joysticks[joystick].axes + axis;
451 int old_active = abs(jaxis->value) > JOY_AXIS_THRESHOLD;
452 int new_active = abs(value) > JOY_AXIS_THRESHOLD;
453 int old_pos = jaxis->value > 0;
454 int new_pos = value > 0;
455 jaxis->value = value;
456 if (old_active && (!new_active || old_pos != new_pos)) {
457 //previously activated direction is no longer active
458 handle_binding_up(old_pos ? &jaxis->positive : &jaxis->negative);
459 }
460 if (new_active && (!old_active || old_pos != new_pos)) {
461 //previously unactivated direction is now active
462 handle_binding_down(new_pos ? &jaxis->positive : &jaxis->negative);
463 }
464 }
465
466 void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16_t deltay)
467 {
468 if (mouse >= MAX_MICE || !current_system) {
469 return;
470 }
471 if (mice[mouse].motion.bind_type == BIND_MOUSE && mice[mouse].motion.subtype_b == PSEUDO_BUTTON_MOTION) {
472 uint8_t target_mouse = mice[mouse].motion.subtype_a;
473 switch(mouse_mode)
474 {
475 case MOUSE_NONE:
476 break;
477 case MOUSE_ABSOLUTE: {
478 if (current_system->mouse_motion_absolute) {
479 float scale_x = (render_emulated_width() * 2.0f) / ((float)render_width());
480 float scale_y = (render_emulated_height() * 2.0f) / ((float)render_height());
481 int32_t adj_x = x * scale_x + 2 * render_overscan_left() - 2 * BORDER_LEFT;
482 int32_t adj_y = y * scale_y + 2 * render_overscan_top() - 4;
483
484 current_system->mouse_motion_absolute(current_system, target_mouse, adj_x, adj_y);
485 }
486 break;
487 }
488 case MOUSE_RELATIVE: {
489 if (current_system->mouse_motion_relative) {
490 current_system->mouse_motion_relative(current_system, target_mouse, deltax, deltay);
491 }
492 break;
493 }
494 case MOUSE_CAPTURE: {
495 if (mouse_captured && current_system->mouse_motion_relative) {
496 current_system->mouse_motion_relative(current_system, target_mouse, deltax, deltay);
497 }
498 break;
499 }
500 }
501 } else {
502 handle_binding_up(&mice[mouse].motion);
503 }
504 }
505
506 void handle_mouseup(int mouse, int button)
507 {
508 if (mouse >= MAX_MICE || button > MAX_MOUSE_BUTTONS || button <= 0) {
509 return;
510 }
511 keybinding * binding = mice[mouse].buttons + button - 1;
512 handle_binding_up(binding);
513 }
514
515 void bindings_release_capture(void)
516 {
517 if (mouse_mode == MOUSE_RELATIVE || (mouse_mode == MOUSE_CAPTURE && mouse_captured)) {
518 render_relative_mouse(0);
519 }
520 keyboard_captured = 0;
521 }
522
523 void bindings_reacquire_capture(void)
524 {
525 if (mouse_mode == MOUSE_RELATIVE || (mouse_mode == MOUSE_CAPTURE && mouse_captured)) {
526 render_relative_mouse(1);
527 }
528 }
529
530 int parse_binding_target(int device_num, char * target, tern_node * padbuttons, tern_node *mousebuttons, uint8_t * subtype_a, uint8_t * subtype_b)
531 {
532 const int gpadslen = strlen("gamepads.");
533 const int mouselen = strlen("mouse.");
534 if (!strncmp(target, "gamepads.", gpadslen)) {
535 int padnum = target[gpadslen] == 'n' ? device_num + 1 : target[gpadslen] - '0';
536 if (padnum >= 1 && padnum <= 8) {
537 int button = tern_find_int(padbuttons, target + gpadslen + 1, 0);
538 if (button) {
539 *subtype_a = padnum;
540 *subtype_b = button;
541 return BIND_GAMEPAD;
542 } else {
543 if (target[gpadslen+1]) {
544 warning("Gamepad mapping string '%s' refers to an invalid button '%s'\n", target, target + gpadslen + 1);
545 } else {
546 warning("Gamepad mapping string '%s' has no button component\n", target);
547 }
548 }
549 } else {
550 warning("Gamepad mapping string '%s' refers to an invalid gamepad number %c\n", target, target[gpadslen]);
551 }
552 } else if(!strncmp(target, "mouse.", mouselen)) {
553 int mousenum = target[mouselen] == 'n' ? device_num + 1 : target[mouselen] - '0';
554 if (mousenum >= 1 && mousenum <= 8) {
555 int button = tern_find_int(mousebuttons, target + mouselen + 1, 0);
556 if (button) {
557 *subtype_a = mousenum;
558 *subtype_b = button;
559 return BIND_MOUSE;
560 } else {
561 if (target[mouselen+1]) {
562 warning("Mouse mapping string '%s' refers to an invalid button '%s'\n", target, target + mouselen + 1);
563 } else {
564 warning("Mouse mapping string '%s' has no button component\n", target);
565 }
566 }
567 } else {
568 warning("Gamepad mapping string '%s' refers to an invalid mouse number %c\n", target, target[mouselen]);
569 }
570 } else if(!strncmp(target, "ui.", strlen("ui."))) {
571 if (!strcmp(target + 3, "vdp_debug_mode")) {
572 *subtype_a = UI_DEBUG_MODE_INC;
573 } else if(!strcmp(target + 3, "vdp_debug_pal")) {
574 //legacy binding, ignore
575 return 0;
576 } else if(!strcmp(target + 3, "enter_debugger")) {
577 *subtype_a = UI_ENTER_DEBUGGER;
578 } else if(!strcmp(target + 3, "save_state")) {
579 *subtype_a = UI_SAVE_STATE;
580 } else if(!strncmp(target + 3, "set_speed.", strlen("set_speed."))) {
581 *subtype_a = UI_SET_SPEED;
582 *subtype_b = atoi(target + 3 + strlen("set_speed."));
583 } else if(!strcmp(target + 3, "next_speed")) {
584 *subtype_a = UI_NEXT_SPEED;
585 } else if(!strcmp(target + 3, "prev_speed")) {
586 *subtype_a = UI_PREV_SPEED;
587 } else if(!strcmp(target + 3, "release_mouse")) {
588 *subtype_a = UI_RELEASE_MOUSE;
589 } else if(!strcmp(target + 3, "toggle_keyboard_captured")) {
590 *subtype_a = UI_TOGGLE_KEYBOARD_CAPTURE;
591 } else if (!strcmp(target + 3, "toggle_fullscreen")) {
592 *subtype_a = UI_TOGGLE_FULLSCREEN;
593 } else if (!strcmp(target + 3, "soft_reset")) {
594 *subtype_a = UI_SOFT_RESET;
595 } else if (!strcmp(target + 3, "reload")) {
596 *subtype_a = UI_RELOAD;
597 } else if (!strcmp(target + 3, "sms_pause")) {
598 *subtype_a = UI_SMS_PAUSE;
599 } else if (!strcmp(target + 3, "screenshot")) {
600 *subtype_a = UI_SCREENSHOT;
601 } else if(!strcmp(target + 3, "exit")) {
602 *subtype_a = UI_EXIT;
603 } else if (!strcmp(target + 3, "plane_debug")) {
604 *subtype_a = UI_PLANE_DEBUG;
605 } else if (!strcmp(target + 3, "vram_debug")) {
606 *subtype_a = UI_VRAM_DEBUG;
607 } else if (!strcmp(target + 3, "cram_debug")) {
608 *subtype_a = UI_CRAM_DEBUG;
609 } else if (!strcmp(target + 3, "compositing_debug")) {
610 *subtype_a = UI_COMPOSITE_DEBUG;
611 } else {
612 warning("Unreconized UI binding type %s\n", target);
613 return 0;
614 }
615 return BIND_UI;
616 } else {
617 warning("Unrecognized binding type %s\n", target);
618 }
619 return 0;
620 }
621
622 void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, tern_node *mousebuttons, char * prefix)
623 {
624 char * curstr = NULL;
625 int len;
626 if (!cur) {
627 return;
628 }
629 char onec[2];
630 if (prefix) {
631 len = strlen(prefix);
632 curstr = malloc(len + 2);
633 memcpy(curstr, prefix, len);
634 } else {
635 curstr = onec;
636 len = 0;
637 }
638 curstr[len] = cur->el;
639 curstr[len+1] = 0;
640 if (cur->el) {
641 process_keys(cur->straight.next, special, padbuttons, mousebuttons, curstr);
642 } else {
643 int keycode = tern_find_int(special, curstr, 0);
644 if (!keycode) {
645 keycode = curstr[0];
646 if (curstr[1] != 0) {
647 warning("%s is not recognized as a key identifier, truncating to %c\n", curstr, curstr[0]);
648 }
649 }
650 char * target = cur->straight.value.ptrval;
651 uint8_t subtype_a = 0, subtype_b = 0;
652 int bindtype = parse_binding_target(0, target, padbuttons, mousebuttons, &subtype_a, &subtype_b);
653 bind_key(keycode, bindtype, subtype_a, subtype_b);
654 }
655 process_keys(cur->left, special, padbuttons, mousebuttons, prefix);
656 process_keys(cur->right, special, padbuttons, mousebuttons, prefix);
657 if (curstr && len) {
658 free(curstr);
659 }
660 }
661
662 void process_speeds(tern_node * cur, char * prefix)
663 {
664 char * curstr = NULL;
665 int len;
666 if (!cur) {
667 return;
668 }
669 char onec[2];
670 if (prefix) {
671 len = strlen(prefix);
672 curstr = malloc(len + 2);
673 memcpy(curstr, prefix, len);
674 } else {
675 curstr = onec;
676 len = 0;
677 }
678 curstr[len] = cur->el;
679 curstr[len+1] = 0;
680 if (cur->el) {
681 process_speeds(cur->straight.next, curstr);
682 } else {
683 char *end;
684 long speed_index = strtol(curstr, &end, 10);
685 if (speed_index < 0 || end == curstr || *end) {
686 warning("%s is not a valid speed index", curstr);
687 } else {
688 if (speed_index >= num_speeds) {
689 speeds = realloc(speeds, sizeof(uint32_t) * (speed_index+1));
690 for(; num_speeds < speed_index + 1; num_speeds++) {
691 speeds[num_speeds] = 0;
692 }
693 }
694 speeds[speed_index] = atoi(cur->straight.value.ptrval);
695 if (speeds[speed_index] < 1) {
696 warning("%s is not a valid speed percentage, setting speed %d to 100", cur->straight.value.ptrval, speed_index);
697 speeds[speed_index] = 100;
698 }
699 }
700 }
701 process_speeds(cur->left, prefix);
702 process_speeds(cur->right, prefix);
703 if (curstr && len) {
704 free(curstr);
705 }
706 }
707
708 typedef struct {
709 tern_node *padbuttons;
710 tern_node *mousebuttons;
711 int mouseidx;
712 } pmb_state;
713
714 void process_mouse_button(char *buttonstr, tern_val value, uint8_t valtype, void *data)
715 {
716 pmb_state *state = data;
717 int buttonnum = atoi(buttonstr);
718 if (buttonnum < 1 || buttonnum > MAX_MOUSE_BUTTONS) {
719 warning("Mouse button %s is out of the supported range of 1-8\n", buttonstr);
720 return;
721 }
722 if (valtype != TVAL_PTR) {
723 warning("Mouse button %s is not a scalar value!\n", buttonstr);
724 return;
725 }
726 buttonnum--;
727 uint8_t subtype_a = 0, subtype_b = 0;
728 int bindtype = parse_binding_target(state->mouseidx, value.ptrval, state->padbuttons, state->mousebuttons, &subtype_a, &subtype_b);
729 mice[state->mouseidx].buttons[buttonnum].bind_type = bindtype;
730 mice[state->mouseidx].buttons[buttonnum].subtype_a = subtype_a;
731 mice[state->mouseidx].buttons[buttonnum].subtype_b = subtype_b;
732 }
733
734 void process_mouse(char *mousenum, tern_val value, uint8_t valtype, void *data)
735 {
736 tern_node **buttonmaps = data;
737 if (valtype != TVAL_NODE) {
738 warning("Binding for mouse %s is a scalar!\n", mousenum);
739 return;
740 }
741 tern_node *mousedef = value.ptrval;
742 tern_node *padbuttons = buttonmaps[0];
743 tern_node *mousebuttons = buttonmaps[1];
744
745 int mouseidx = atoi(mousenum);
746 if (mouseidx < 0 || mouseidx >= MAX_MICE) {
747 warning("Mouse numbers must be between 0 and %d, but %d is not\n", MAX_MICE, mouseidx);
748 return;
749 }
750 char *motion = tern_find_ptr(mousedef, "motion");
751 if (motion) {
752 uint8_t subtype_a = 0, subtype_b = 0;
753 int bindtype = parse_binding_target(mouseidx, motion, padbuttons, mousebuttons, &subtype_a, &subtype_b);
754 mice[mouseidx].motion.bind_type = bindtype;
755 mice[mouseidx].motion.subtype_a = subtype_a;
756 mice[mouseidx].motion.subtype_b = subtype_b;
757 }
758 tern_node *buttons = tern_find_path(mousedef, "buttons\0\0", TVAL_NODE).ptrval;
759 if (buttons) {
760 pmb_state state = {padbuttons, mousebuttons, mouseidx};
761 tern_foreach(buttons, process_mouse_button, &state);
762 }
763 }
764
765 typedef struct {
766 int padnum;
767 tern_node *padbuttons;
768 tern_node *mousebuttons;
769 } pad_button_state;
770
771
772 static long map_warning_pad = -1;
773 void process_pad_button(char *key, tern_val val, uint8_t valtype, void *data)
774 {
775 pad_button_state *state = data;
776 int hostpadnum = state->padnum;
777 if (valtype != TVAL_PTR) {
778 warning("Pad button %s has a non-scalar value\n", key);
779 return;
780 }
781 uint8_t subtype_a = 0, subtype_b = 0;
782 int bindtype = parse_binding_target(hostpadnum, val.ptrval, state->padbuttons, state->mousebuttons, &subtype_a, &subtype_b);
783 char *end;
784 long hostbutton = strtol(key, &end, 10);
785 if (*end) {
786 //key is not a valid base 10 integer
787 hostbutton = render_translate_input_name(hostpadnum, key, 0);
788 if (hostbutton < 0) {
789 if (hostbutton == RENDER_INVALID_NAME) {
790 warning("%s is not a valid gamepad input name\n", key);
791 } else if (hostbutton == RENDER_NOT_MAPPED && hostpadnum != map_warning_pad) {
792 warning("No SDL 2 mapping exists for input %s on gamepad %d\n", key, hostpadnum);
793 map_warning_pad = hostpadnum;
794 }
795 return;
796 }
797 if (hostbutton & RENDER_DPAD_BIT) {
798 bind_dpad(hostpadnum, render_dpad_part(hostbutton), render_direction_part(hostbutton), bindtype, subtype_a, subtype_b);
799 return;
800 } else if (hostbutton & RENDER_AXIS_BIT) {
801 bind_axis(hostpadnum, render_axis_part(hostbutton), 1, bindtype, subtype_a, subtype_b);
802 return;
803 }
804 }
805 bind_button(hostpadnum, hostbutton, bindtype, subtype_a, subtype_b);
806 }
807
808 void process_pad_axis(char *key, tern_val val, uint8_t valtype, void *data)
809 {
810 key = strdup(key);
811 pad_button_state *state = data;
812 int hostpadnum = state->padnum;
813 if (valtype != TVAL_PTR) {
814 warning("Mapping for axis %s has a non-scalar value", key);
815 return;
816 }
817 uint8_t subtype_a = 0, subtype_b = 0;
818 int bindtype = parse_binding_target(hostpadnum, val.ptrval, state->padbuttons, state->mousebuttons, &subtype_a, &subtype_b);
819 char *modifier = strchr(key, '.');
820 int positive = 1;
821 if (modifier) {
822 *modifier = 0;
823 modifier++;
824 if (!strcmp("negative", modifier)) {
825 positive = 0;
826 } else if(strcmp("positive", modifier)) {
827 warning("Invalid axis modifier %s for axis %s on pad %d\n", modifier, key, hostpadnum);
828 }
829 }
830 char *end;
831 long axis = strtol(key, &end, 10);
832 if (*end) {
833 //key is not a valid base 10 integer
834 axis = render_translate_input_name(hostpadnum, key, 1);
835 if (axis < 0) {
836 if (axis == RENDER_INVALID_NAME) {
837 warning("%s is not a valid gamepad input name\n", key);
838 } else if (axis == RENDER_NOT_MAPPED && hostpadnum != map_warning_pad) {
839 warning("No SDL 2 mapping exists for input %s on gamepad %d\n", key, hostpadnum);
840 map_warning_pad = hostpadnum;
841 }
842 goto done;
843 }
844 if (axis & RENDER_DPAD_BIT) {
845 bind_dpad(hostpadnum, render_dpad_part(axis), render_direction_part(axis), bindtype, subtype_a, subtype_b);
846 goto done;
847 } else if (axis & RENDER_AXIS_BIT) {
848 axis = render_axis_part(axis);
849 } else {
850 bind_button(hostpadnum, axis, bindtype, subtype_a, subtype_b);
851 goto done;
852 }
853 }
854 bind_axis(hostpadnum, axis, positive, bindtype, subtype_a, subtype_b);
855 done:
856 free(key);
857 return;
858 }
859
860 static tern_node *get_pad_buttons()
861 {
862 static tern_node *padbuttons;
863 if (!padbuttons) {
864 padbuttons = tern_insert_int(NULL, ".up", DPAD_UP);
865 padbuttons = tern_insert_int(padbuttons, ".down", DPAD_DOWN);
866 padbuttons = tern_insert_int(padbuttons, ".left", DPAD_LEFT);
867 padbuttons = tern_insert_int(padbuttons, ".right", DPAD_RIGHT);
868 padbuttons = tern_insert_int(padbuttons, ".a", BUTTON_A);
869 padbuttons = tern_insert_int(padbuttons, ".b", BUTTON_B);
870 padbuttons = tern_insert_int(padbuttons, ".c", BUTTON_C);
871 padbuttons = tern_insert_int(padbuttons, ".x", BUTTON_X);
872 padbuttons = tern_insert_int(padbuttons, ".y", BUTTON_Y);
873 padbuttons = tern_insert_int(padbuttons, ".z", BUTTON_Z);
874 padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START);
875 padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE);
876 }
877 return padbuttons;
878 }
879
880 static tern_node *get_mouse_buttons()
881 {
882 static tern_node *mousebuttons;
883 if (!mousebuttons) {
884 mousebuttons = tern_insert_int(NULL, ".left", MOUSE_LEFT);
885 mousebuttons = tern_insert_int(mousebuttons, ".middle", MOUSE_MIDDLE);
886 mousebuttons = tern_insert_int(mousebuttons, ".right", MOUSE_RIGHT);
887 mousebuttons = tern_insert_int(mousebuttons, ".start", MOUSE_START);
888 mousebuttons = tern_insert_int(mousebuttons, ".motion", PSEUDO_BUTTON_MOTION);
889 }
890 return mousebuttons;
891 }
892
893 tern_node *get_binding_node_for_pad(int padnum)
894 {
895 if (padnum > MAX_JOYSTICKS) {
896 return NULL;
897 }
898 tern_node * pads = tern_find_path(config, "bindings\0pads\0", TVAL_NODE).ptrval;
899 if (!pads) {
900 return NULL;
901 }
902 char numstr[11];
903 sprintf(numstr, "%d", padnum);
904 tern_node * pad = tern_find_node(pads, numstr);
905 if (!pad) {
906 char *type_id = render_joystick_type_id(padnum);
907 pad = tern_find_node(pads, type_id);
908 free(type_id);
909 }
910 if (!pad) {
911 controller_info info = get_controller_info(padnum);
912 char *key = make_controller_type_key(&info);
913 pad = tern_find_node(pads, key);
914 free(key);
915 }
916 if (!pad) {
917 pad = tern_find_node(pads, "default");
918 }
919 return pad;
920 }
921
922 void handle_joy_added(int joystick)
923 {
924 tern_node *pad = get_binding_node_for_pad(joystick);
925 if (!pad) {
926 return;
927 }
928 tern_node * dpad_node = tern_find_node(pad, "dpads");
929 if (dpad_node) {
930 for (int dpad = 0; dpad < 10; dpad++)
931 {
932 char numstr[2] = {dpad + '0', 0};
933 tern_node * pad_dpad = tern_find_node(dpad_node, numstr);
934 char * dirs[] = {"up", "down", "left", "right"};
935 //TODO: Support controllers that have d-pads implemented as analog axes or buttons
936 int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
937 for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) {
938 char * target = tern_find_ptr(pad_dpad, dirs[dir]);
939 if (target) {
940 uint8_t subtype_a = 0, subtype_b = 0;
941 int bindtype = parse_binding_target(joystick, target, get_pad_buttons(), get_mouse_buttons(), &subtype_a, &subtype_b);
942 bind_dpad(joystick, dpad, dirnums[dir], bindtype, subtype_a, subtype_b);
943 }
944 }
945 }
946 }
947 tern_node *button_node = tern_find_node(pad, "buttons");
948 if (button_node) {
949 pad_button_state state = {
950 .padnum = joystick,
951 .padbuttons = get_pad_buttons(),
952 .mousebuttons = get_mouse_buttons()
953 };
954 tern_foreach(button_node, process_pad_button, &state);
955 }
956 tern_node *axes_node = tern_find_node(pad, "axes");
957 if (axes_node) {
958 pad_button_state state = {
959 .padnum = joystick,
960 .padbuttons = get_pad_buttons(),
961 .mousebuttons = get_mouse_buttons()
962 };
963 tern_foreach(axes_node, process_pad_axis, &state);
964 }
965 }
966
967 //only handles keyboards and mice as gamepads are handled on hotplug events
968 void set_bindings(void)
969 {
970 tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP);
971 special = tern_insert_int(special, "down", RENDERKEY_DOWN);
972 special = tern_insert_int(special, "left", RENDERKEY_LEFT);
973 special = tern_insert_int(special, "right", RENDERKEY_RIGHT);
974 special = tern_insert_int(special, "enter", '\r');
975 special = tern_insert_int(special, "space", ' ');
976 special = tern_insert_int(special, "tab", '\t');
977 special = tern_insert_int(special, "backspace", '\b');
978 special = tern_insert_int(special, "esc", RENDERKEY_ESC);
979 special = tern_insert_int(special, "delete", RENDERKEY_DEL);
980 special = tern_insert_int(special, "lshift", RENDERKEY_LSHIFT);
981 special = tern_insert_int(special, "rshift", RENDERKEY_RSHIFT);
982 special = tern_insert_int(special, "lctrl", RENDERKEY_LCTRL);
983 special = tern_insert_int(special, "rctrl", RENDERKEY_RCTRL);
984 special = tern_insert_int(special, "lalt", RENDERKEY_LALT);
985 special = tern_insert_int(special, "ralt", RENDERKEY_RALT);
986 special = tern_insert_int(special, "home", RENDERKEY_HOME);
987 special = tern_insert_int(special, "end", RENDERKEY_END);
988 special = tern_insert_int(special, "pageup", RENDERKEY_PAGEUP);
989 special = tern_insert_int(special, "pagedown", RENDERKEY_PAGEDOWN);
990 special = tern_insert_int(special, "f1", RENDERKEY_F1);
991 special = tern_insert_int(special, "f2", RENDERKEY_F2);
992 special = tern_insert_int(special, "f3", RENDERKEY_F3);
993 special = tern_insert_int(special, "f4", RENDERKEY_F4);
994 special = tern_insert_int(special, "f5", RENDERKEY_F5);
995 special = tern_insert_int(special, "f6", RENDERKEY_F6);
996 special = tern_insert_int(special, "f7", RENDERKEY_F7);
997 special = tern_insert_int(special, "f8", RENDERKEY_F8);
998 special = tern_insert_int(special, "f9", RENDERKEY_F9);
999 special = tern_insert_int(special, "f10", RENDERKEY_F10);
1000 special = tern_insert_int(special, "f11", RENDERKEY_F11);
1001 special = tern_insert_int(special, "f12", RENDERKEY_F12);
1002 special = tern_insert_int(special, "select", RENDERKEY_SELECT);
1003 special = tern_insert_int(special, "play", RENDERKEY_PLAY);
1004 special = tern_insert_int(special, "search", RENDERKEY_SEARCH);
1005 special = tern_insert_int(special, "back", RENDERKEY_BACK);
1006 special = tern_insert_int(special, "np0", RENDERKEY_NP0);
1007 special = tern_insert_int(special, "np1", RENDERKEY_NP1);
1008 special = tern_insert_int(special, "np2", RENDERKEY_NP2);
1009 special = tern_insert_int(special, "np3", RENDERKEY_NP3);
1010 special = tern_insert_int(special, "np4", RENDERKEY_NP4);
1011 special = tern_insert_int(special, "np5", RENDERKEY_NP5);
1012 special = tern_insert_int(special, "np6", RENDERKEY_NP6);
1013 special = tern_insert_int(special, "np7", RENDERKEY_NP7);
1014 special = tern_insert_int(special, "np8", RENDERKEY_NP8);
1015 special = tern_insert_int(special, "np9", RENDERKEY_NP9);
1016 special = tern_insert_int(special, "np/", RENDERKEY_NP_DIV);
1017 special = tern_insert_int(special, "np*", RENDERKEY_NP_MUL);
1018 special = tern_insert_int(special, "np-", RENDERKEY_NP_MIN);
1019 special = tern_insert_int(special, "np+", RENDERKEY_NP_PLUS);
1020 special = tern_insert_int(special, "npenter", RENDERKEY_NP_ENTER);
1021 special = tern_insert_int(special, "np.", RENDERKEY_NP_STOP);
1022
1023 tern_node *padbuttons = get_pad_buttons();
1024
1025 tern_node *mousebuttons = get_mouse_buttons();
1026
1027 tern_node * keys = tern_find_path(config, "bindings\0keys\0", TVAL_NODE).ptrval;
1028 process_keys(keys, special, padbuttons, mousebuttons, NULL);
1029 tern_free(special);
1030
1031 memset(mice, 0, sizeof(mice));
1032 tern_node * mice = tern_find_path(config, "bindings\0mice\0", TVAL_NODE).ptrval;
1033 if (mice) {
1034 tern_node *buttonmaps[2] = {padbuttons, mousebuttons};
1035 tern_foreach(mice, process_mouse, buttonmaps);
1036 }
1037 tern_node * speed_nodes = tern_find_path(config, "clocks\0speeds\0", TVAL_NODE).ptrval;
1038 speeds = malloc(sizeof(uint32_t));
1039 speeds[0] = 100;
1040 process_speeds(speed_nodes, NULL);
1041 for (int i = 0; i < num_speeds; i++)
1042 {
1043 if (!speeds[i]) {
1044 warning("Speed index %d was not set to a valid percentage!", i);
1045 speeds[i] = 100;
1046 }
1047 }
1048 }
1049
1050 void bindings_set_mouse_mode(uint8_t mode)
1051 {
1052 mouse_mode = mode;
1053 if (mode == MOUSE_RELATIVE) {
1054 render_relative_mouse(1);
1055 }
1056 }