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