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