comparison bindings.c @ 1583:430dd12e4010

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