1 #include "joystick.h"
2
3 #include "core/log.h"
4 #include "core/speed.h"
5 #include "core/time.h"
6 #include "game/system.h"
7 #include "graphics/window.h"
8 #include "input/hotkey.h"
9 #include "input/keyboard.h"
10 #include "input/mouse.h"
11 #include "input/scroll.h"
12
13 #include <math.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #define HOTKEY_OFFSET MAPPING_ACTION_ROTATE_MAP_LEFT
18 #define MAX_HOTKEYS 6
19 #define MAX_AXIS 8
20 #define MAX_BUTTONS 20
21 #define MAX_TRACKBALLS 4
22 #define MAX_HATS 4
23 #define MAX_CONTROLLERS 6
24
25 #define DEADZONE 2000.0f
26 #define AXIS_MAX_THRESHOLD 1300
27 #define AXIS_MAX_VALUE 32767
28
29 // Note: this ratio is untested and may be under or oversensitive
30 #define TRACKBALL_TO_AXIS_RATIO 5
31
32 typedef int joystick_axis;
33 typedef int joystick_button;
34
35 enum {
36 JOYSTICK_TRACKBALL_X_POSITIVE = 0,
37 JOYSTICK_TRACKBALL_X_NEGATIVE = 1,
38 JOYSTICK_TRACKBALL_Y_POSITIVE = 2,
39 JOYSTICK_TRACKBALL_Y_NEGATIVE = 3,
40 };
41
42 enum {
43 DIRECTION_UP = 0,
44 DIRECTION_LEFT = 1,
45 DIRECTION_DOWN = 2,
46 DIRECTION_RIGHT = 3,
47 NUM_DIRECTIONS = 4
48 };
49
50 typedef enum {
51 INPUT_STATE_IS_UP,
52 INPUT_STATE_WENT_DOWN,
53 INPUT_STATE_IS_DOWN,
54 INPUT_STATE_WENT_UP
55 } input_state;
56
57 typedef struct {
58 joystick_button top;
59 joystick_button left;
60 joystick_button bottom;
61 joystick_button right;
62 } joystick_hat;
63
64 typedef struct {
65 int delta_x;
66 int delta_y;
67 } joystick_trackball;
68
69 typedef struct {
70 int id;
71 int connected;
72 joystick_model *model;
73 joystick_axis axis[MAX_AXIS];
74 joystick_button button[MAX_BUTTONS];
75 joystick_trackball trackball[MAX_TRACKBALLS];
76 joystick_hat hat[MAX_HATS];
77 } joystick_info;
78
79 typedef struct {
80 joystick_element element;
81 int value;
82 input_state state;
83 } mapped_input;
84
85 enum {
86 CURSOR_SLOWDOWN_FASTER = 1024,
87 CURSOR_SLOWDOWN_NORMAL = 4096,
88 CURSOR_SLOWDOWN_SLOWER = 8192
89 };
90
91 static struct {
92 joystick_model connected_models[MAX_CONTROLLERS];
93 joystick_info joystick[MAX_CONTROLLERS];
94 int connected_joysticks;
95 struct {
96 speed_type x_speed;
97 speed_type y_speed;
98 int left_button;
99 int middle_button;
100 int right_button;
101 scroll_state scroll;
102 time_millis last_scroll_time;
103 } mouse;
104 mapped_input map_scroll[NUM_DIRECTIONS];
105 mapped_input joystick_hotkey[MAX_HOTKEYS];
106 mapped_input virtual_keyboard;
107 mapped_input touch_mode;
108 } data;
109
110 static hotkey_action JOYSTICK_MAPPING_TO_HOTKEY_ACTION[MAX_HOTKEYS] = {
111 HOTKEY_ROTATE_MAP_LEFT,
112 HOTKEY_ROTATE_MAP_RIGHT,
113 HOTKEY_INCREASE_GAME_SPEED,
114 HOTKEY_DECREASE_GAME_SPEED,
115 HOTKEY_TOGGLE_PAUSE,
116 HOTKEY_CYCLE_LEGION
117 };
118
update_hat(joystick_hat * hat,joystick_hat_position position)119 static void update_hat(joystick_hat *hat, joystick_hat_position position)
120 {
121 hat->top = (position & JOYSTICK_HAT_UP) ? 1 : 0;
122 hat->left = (position & JOYSTICK_HAT_LEFT) ? 1 : 0;
123 hat->bottom = (position & JOYSTICK_HAT_DOWN) ? 1 : 0;
124 hat->right = (position & JOYSTICK_HAT_RIGHT) ? 1 : 0;
125 }
126
update_trackball(joystick_trackball * trackball,int delta_x,int delta_y)127 static void update_trackball(joystick_trackball *trackball, int delta_x, int delta_y)
128 {
129 trackball->delta_x += delta_x;
130 trackball->delta_y += delta_y;
131 }
132
reset_joystick_state(joystick_info * joystick)133 static void reset_joystick_state(joystick_info *joystick)
134 {
135 memset(joystick->axis, 0, sizeof(int) * MAX_AXIS);
136 memset(joystick->button, 0, sizeof(joystick_button) * MAX_BUTTONS);
137 memset(joystick->trackball, 0, sizeof(joystick_trackball) * MAX_TRACKBALLS);
138 memset(joystick->hat, 0, sizeof(joystick_hat) * MAX_HATS);
139 }
140
get_free_joystick(void)141 static joystick_info *get_free_joystick(void)
142 {
143 for (int i = 0; i < MAX_CONTROLLERS; ++i) {
144 if (!data.joystick[i].connected) {
145 return &data.joystick[i];
146 }
147 }
148 return 0;
149 }
150
get_model_by_guid(const char * guid)151 static joystick_model *get_model_by_guid(const char *guid)
152 {
153 for (int i = 0; i < MAX_CONTROLLERS; ++i) {
154 joystick_model *model = &data.connected_models[i];
155 if (strcmp(guid, model->guid) == 0) {
156 return model;
157 }
158 }
159 return 0;
160 }
161
joystick_has_model(const char * guid)162 int joystick_has_model(const char *guid)
163 {
164 return get_model_by_guid(guid) != 0;
165 }
166
joystick_add_model(const joystick_model * model)167 void joystick_add_model(const joystick_model *model)
168 {
169 for (int i = 0; i < MAX_CONTROLLERS; ++i) {
170 if (!data.connected_models[i].connected_joysticks) {
171 memcpy(&data.connected_models[i], model, sizeof(joystick_model));
172 return;
173 }
174 }
175 }
176
joystick_add(int joystick_id,const char * guid)177 int joystick_add(int joystick_id, const char *guid)
178 {
179 joystick_info *joystick = get_free_joystick();
180 joystick_model *model = get_model_by_guid(guid);
181 if (!joystick || !model) {
182 return 0;
183 }
184 joystick->id = joystick_id;
185 joystick->model = model;
186 joystick->connected = 1;
187 model->connected_joysticks++;
188 data.connected_joysticks++;
189 log_info("Joystick added with name", model->name, 0);
190 return 1;
191 }
192
get_joystick_by_id(int joystick_id)193 static joystick_info *get_joystick_by_id(int joystick_id)
194 {
195 for (int i = 0; i < MAX_CONTROLLERS; ++i) {
196 if (data.joystick[i].id == joystick_id && data.joystick[i].connected) {
197 return &data.joystick[i];
198 }
199 }
200 return 0;
201 }
202
joystick_is_active(int joystick_id)203 int joystick_is_active(int joystick_id)
204 {
205 return get_joystick_by_id(joystick_id) != 0;
206 }
207
joystick_remove(int joystick_id)208 int joystick_remove(int joystick_id)
209 {
210 joystick_info *joystick = get_joystick_by_id(joystick_id);
211 if (!joystick) {
212 return 0;
213 }
214 joystick->connected = 0;
215 joystick->model->connected_joysticks--;
216 const char *name = joystick->model->name;
217 joystick->model = 0;
218 reset_joystick_state(joystick);
219 data.connected_joysticks--;
220 log_info("Joystick removed with name", name, 0);
221 return 1;
222 }
223
joystick_update_element(int joystick_id,joystick_element element,int element_id,int value1,int value2)224 void joystick_update_element(int joystick_id, joystick_element element, int element_id, int value1, int value2)
225 {
226 joystick_info *joystick = get_joystick_by_id(joystick_id);
227 if (!joystick) {
228 return;
229 }
230 switch (element) {
231 case JOYSTICK_ELEMENT_BUTTON:
232 joystick->button[element_id] = value1;
233 break;
234 case JOYSTICK_ELEMENT_HAT:
235 update_hat(&joystick->hat[element_id], value1);
236 break;
237 case JOYSTICK_ELEMENT_AXIS:
238 joystick->axis[element_id] = (abs(value1) > AXIS_MAX_THRESHOLD) ? value1 : 0;
239 break;
240 case JOYSTICK_ELEMENT_TRACKBALL:
241 update_trackball(&joystick->trackball[element_id], value1, value2);
242 break;
243 default:
244 log_info("Trying to update wrong joystick element", 0, element);
245 break;
246 }
247 }
248
get_input_for_mapping(const joystick_info * joystick,const mapping_element * mapping,mapped_input * input)249 static int get_input_for_mapping(const joystick_info *joystick, const mapping_element *mapping, mapped_input *input)
250 {
251 for (int j = 0; j < JOYSTICK_MAPPING_ELEMENTS_MAX; ++j) {
252 joystick_element current_element = mapping->element[j].type;
253 int current_value = 0;
254 int element_id = mapping->element[j].id;
255 int element_position = mapping->element[j].position;
256 switch (current_element) {
257 case JOYSTICK_ELEMENT_AXIS:
258 if (element_position == JOYSTICK_AXIS_POSITIVE) {
259 current_value = (joystick->axis[element_id] > 0) ? joystick->axis[element_id] : 0;
260 } else {
261 current_value = (joystick->axis[element_id] < 0) ? -joystick->axis[element_id] : 0;
262 }
263 break;
264 case JOYSTICK_ELEMENT_TRACKBALL:
265 switch (element_position) {
266 case JOYSTICK_TRACKBALL_X_POSITIVE:
267 if (joystick->trackball[element_id].delta_x > 0) {
268 current_value = joystick->trackball[element_id].delta_x;
269 }
270 break;
271 case JOYSTICK_TRACKBALL_X_NEGATIVE:
272 if (joystick->trackball[element_id].delta_x < 0) {
273 current_value = -joystick->trackball[element_id].delta_x;
274 }
275 break;
276 case JOYSTICK_TRACKBALL_Y_POSITIVE:
277 if (joystick->trackball[element_id].delta_y > 0) {
278 current_value = joystick->trackball[element_id].delta_y;
279 }
280 break;
281 case JOYSTICK_TRACKBALL_Y_NEGATIVE:
282 if (joystick->trackball[element_id].delta_y < 0) {
283 current_value = -joystick->trackball[element_id].delta_y;
284 }
285 break;
286 }
287 break;
288 case JOYSTICK_ELEMENT_BUTTON:
289 current_value = joystick->button[element_id];
290 break;
291 case JOYSTICK_ELEMENT_HAT:
292 {
293 const joystick_hat *hat = &joystick->hat[element_id];
294 switch (element_position) {
295 case JOYSTICK_HAT_UP:
296 current_value = hat->top;
297 break;
298 case JOYSTICK_HAT_LEFT:
299 current_value = hat->left;
300 break;
301 case JOYSTICK_HAT_DOWN:
302 current_value = hat->bottom;
303 break;
304 case JOYSTICK_HAT_RIGHT:
305 current_value = hat->right;
306 break;
307 default:
308 current_value = 0;
309 log_info("Invalid hat value for hat", 0, element_id);
310 break;
311 }
312 break;
313 }
314 default:
315 continue;
316 }
317 if (current_value == 0) {
318 input->element = JOYSTICK_ELEMENT_NONE;
319 input->value = 0;
320 return 0;
321 }
322 // Element precedence. When two joystick elements are required for the input
323 // to be registered, the value is obtained from the highest priority element.
324 // Element priority in descending order: axis, trackball, button, hat
325 if (input->element == JOYSTICK_ELEMENT_NONE || current_element < input->element) {
326 input->element = current_element;
327 input->value = current_value;
328 }
329 }
330 return input->value != 0;
331 }
332
set_input_state(mapped_input * input)333 static void set_input_state(mapped_input *input)
334 {
335 if (input->value) {
336 if (input->state == INPUT_STATE_IS_UP || input->state == INPUT_STATE_WENT_UP) {
337 input->state = INPUT_STATE_WENT_DOWN;
338 } else {
339 input->state = INPUT_STATE_IS_DOWN;
340 }
341 } else {
342 if (input->state == INPUT_STATE_IS_DOWN || input->state == INPUT_STATE_WENT_DOWN) {
343 input->state = INPUT_STATE_WENT_UP;
344 } else {
345 input->state = INPUT_STATE_IS_UP;
346 }
347 }
348 }
349
get_joystick_input_for_action(mapping_action action,mapped_input * input)350 static int get_joystick_input_for_action(mapping_action action, mapped_input *input)
351 {
352 static mapped_input dummy_input = {0};
353 if (!input) {
354 input = &dummy_input;
355 }
356
357 input->value = 0;
358 input->element = JOYSTICK_ELEMENT_NONE;
359
360 for (int i = 0; i < MAX_CONTROLLERS; ++i) {
361 const joystick_info *joystick = &data.joystick[i];
362 if (!joystick->connected) {
363 continue;
364 }
365 const joystick_model *model = joystick->model;
366 for (int j = 0; j < model->num_mappings; j++) {
367 if (model->mapping[j].action == action && get_input_for_mapping(joystick, &model->mapping[j], input)) {
368 set_input_state(input);
369 return 1;
370 }
371 }
372 }
373 set_input_state(input);
374 return 0;
375 }
376
translate_input_for_element(mapped_input * input,joystick_element translated_element)377 static void translate_input_for_element(mapped_input *input, joystick_element translated_element)
378 {
379 if (input->element == translated_element) {
380 return;
381 }
382 if (translated_element == JOYSTICK_ELEMENT_AXIS) {
383 switch (input->element)
384 {
385 case JOYSTICK_ELEMENT_TRACKBALL:
386 input->value *= TRACKBALL_TO_AXIS_RATIO;
387 break;
388 default:
389 input->value *= AXIS_MAX_VALUE;
390 break;
391 }
392 } else if (translated_element == JOYSTICK_ELEMENT_TRACKBALL) {
393 switch (input->element) {
394 case JOYSTICK_ELEMENT_AXIS:
395 input->value /= TRACKBALL_TO_AXIS_RATIO;
396 break;
397 default:
398 input->value *= TRACKBALL_TO_AXIS_RATIO;
399 break;
400 }
401 } else if (translated_element == JOYSTICK_ELEMENT_BUTTON || translated_element == JOYSTICK_ELEMENT_HAT) {
402 input->value = (input->value != 0) ? 1 : 0;
403 }
404 input->element = translated_element;
405 }
406
rescale_axis(mapped_input * inputs)407 static int rescale_axis(mapped_input *inputs)
408 {
409 // Radial and scaled dead_zone
410 // http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html
411 // Input and output values go from -AXIS_MAX_VALUE to AXIS_MAX_VALUE
412
413 // The maximum is adjusted to account for SCE_CTRL_MODE_DIGITALANALOG_WIDE
414 // where a reported maximum axis value corresponds to 80% of the full range
415 // of motion of the analog stick
416 float analog_x = (float) inputs[DIRECTION_RIGHT].value - inputs[DIRECTION_LEFT].value;
417 float analog_y = (float) inputs[DIRECTION_DOWN].value - inputs[DIRECTION_UP].value;
418
419 inputs[DIRECTION_UP].value = 0;
420 inputs[DIRECTION_LEFT].value = 0;
421 inputs[DIRECTION_DOWN].value = 0;
422 inputs[DIRECTION_RIGHT].value = 0;
423
424 float magnitude = sqrtf(analog_x * analog_x + analog_y * analog_y);
425 if (magnitude < DEADZONE) {
426 return 0;
427 }
428 // Adjust maximum magnitude
429 float abs_analog_x = fabsf(analog_x);
430 float abs_analog_y = fabsf(analog_y);
431 float max_x;
432 float max_y;
433 if (abs_analog_x > abs_analog_y) {
434 max_x = AXIS_MAX_VALUE;
435 max_y = (AXIS_MAX_VALUE * analog_y) / abs_analog_x;
436 } else {
437 max_x = (AXIS_MAX_VALUE * analog_x) / abs_analog_y;
438 max_y = AXIS_MAX_VALUE;
439 }
440 float maximum = sqrtf(max_x * max_x + max_y * max_y);
441 if (maximum > 1.25f * AXIS_MAX_VALUE) {
442 maximum = 1.25f * AXIS_MAX_VALUE;
443 }
444 if (maximum < magnitude) {
445 maximum = magnitude;
446 }
447
448 // Find scaled axis values with magnitudes between zero and maximum
449 float scaling_factor = maximum / magnitude * (magnitude - DEADZONE) / (maximum - DEADZONE);
450 analog_x = (analog_x * scaling_factor);
451 analog_y = (analog_y * scaling_factor);
452
453 // Clamp to ensure results will never exceed the max_axis value
454 float clamping_factor = 1.0f;
455 abs_analog_x = fabsf(analog_x);
456 abs_analog_y = fabsf(analog_y);
457 if (abs_analog_x > AXIS_MAX_VALUE || abs_analog_y > AXIS_MAX_VALUE) {
458 if (abs_analog_x > abs_analog_y) {
459 clamping_factor = AXIS_MAX_VALUE / abs_analog_x;
460 } else {
461 clamping_factor = AXIS_MAX_VALUE / abs_analog_y;
462 }
463 }
464 if (analog_y > 0.0f) {
465 inputs[DIRECTION_DOWN].value = (int) (clamping_factor * analog_y);
466 } else if (analog_y < 0.0f) {
467 inputs[DIRECTION_UP].value = (int) (clamping_factor * -analog_y);
468 }
469 if (analog_x > 0.0f) {
470 inputs[DIRECTION_RIGHT].value = (int) (clamping_factor * analog_x);
471 } else if (analog_x < 0.0f) {
472 inputs[DIRECTION_LEFT].value = (int) (clamping_factor * -analog_x);
473 }
474 return 1;
475 }
476
get_highest_priority_element(mapped_input * inputs,int total_inputs)477 static joystick_element get_highest_priority_element(mapped_input *inputs, int total_inputs)
478 {
479 joystick_element highest_priority = inputs[0].element;
480 for (int i = 1; i < total_inputs; ++i) {
481 if (inputs[i].element < highest_priority && inputs[i].element != JOYSTICK_ELEMENT_NONE) {
482 highest_priority = inputs[i].element;
483 }
484 }
485 return highest_priority;
486 }
487
translate_mapping_reset(void)488 static int translate_mapping_reset(void)
489 {
490 return get_joystick_input_for_action(MAPPING_ACTION_RESET_MAPPING, 0);
491 }
492
translate_mouse_cursor_position(void)493 static int translate_mouse_cursor_position(void)
494 {
495 mapped_input cursor_input[NUM_DIRECTIONS] = {0};
496
497 int handled = get_joystick_input_for_action(MAPPING_ACTION_MOUSE_CURSOR_UP, &cursor_input[DIRECTION_UP]);
498 handled |= get_joystick_input_for_action(MAPPING_ACTION_MOUSE_CURSOR_LEFT, &cursor_input[DIRECTION_LEFT]);
499 handled |= get_joystick_input_for_action(MAPPING_ACTION_MOUSE_CURSOR_DOWN, &cursor_input[DIRECTION_DOWN]);
500 handled |= get_joystick_input_for_action(MAPPING_ACTION_MOUSE_CURSOR_RIGHT, &cursor_input[DIRECTION_RIGHT]);
501 if (!handled) {
502 speed_clear(&data.mouse.x_speed);
503 speed_clear(&data.mouse.y_speed);
504 return 0;
505 }
506
507 // All mouse cursor input will internally be treated as a joystick axis
508 translate_input_for_element(&cursor_input[DIRECTION_UP], JOYSTICK_ELEMENT_AXIS);
509 translate_input_for_element(&cursor_input[DIRECTION_LEFT], JOYSTICK_ELEMENT_AXIS);
510 translate_input_for_element(&cursor_input[DIRECTION_DOWN], JOYSTICK_ELEMENT_AXIS);
511 translate_input_for_element(&cursor_input[DIRECTION_RIGHT], JOYSTICK_ELEMENT_AXIS);
512
513 if (!rescale_axis(cursor_input)) {
514 speed_clear(&data.mouse.x_speed);
515 speed_clear(&data.mouse.y_speed);
516 return 0;
517 }
518
519 double slowdown = CURSOR_SLOWDOWN_NORMAL;
520 if (get_joystick_input_for_action(MAPPING_ACTION_FASTER_MOUSE_CURSOR_SPEED, 0)) {
521 slowdown = CURSOR_SLOWDOWN_FASTER;
522 } else if (get_joystick_input_for_action(MAPPING_ACTION_SLOWER_MOUSE_CURSOR_SPEED, 0)) {
523 slowdown = CURSOR_SLOWDOWN_SLOWER;
524 }
525 int delta_x = cursor_input[DIRECTION_RIGHT].value - cursor_input[DIRECTION_LEFT].value;
526 int delta_y = cursor_input[DIRECTION_DOWN].value - cursor_input[DIRECTION_UP].value;
527
528 speed_set_target(&data.mouse.x_speed, delta_x / slowdown, SPEED_CHANGE_IMMEDIATE, 1);
529 speed_set_target(&data.mouse.y_speed, delta_y / slowdown, SPEED_CHANGE_IMMEDIATE, 1);
530
531 delta_x = speed_get_delta(&data.mouse.x_speed);
532 delta_y = speed_get_delta(&data.mouse.y_speed);
533
534 if (!delta_x && !delta_y) {
535 return 1;
536 }
537
538 system_move_mouse_cursor(delta_x, delta_y);
539 return 1;
540 }
541
translate_mouse_button_presses(void)542 static int translate_mouse_button_presses(void)
543 {
544 int handled = 0;
545 int button = get_joystick_input_for_action(MAPPING_ACTION_LEFT_MOUSE_BUTTON, 0);
546 if (button != data.mouse.left_button) {
547 data.mouse.left_button = button;
548 mouse_set_left_down(button);
549 handled = 1;
550 }
551 button = get_joystick_input_for_action(MAPPING_ACTION_RIGHT_MOUSE_BUTTON, 0);
552 if (button != data.mouse.right_button) {
553 data.mouse.right_button = button;
554 mouse_set_right_down(button);
555 handled = 1;
556 }
557 return handled;
558 }
559
translate_mouse(void)560 static int translate_mouse(void)
561 {
562 int handled = 0;
563 handled |= translate_mouse_cursor_position();
564 handled |= translate_mouse_button_presses();
565 return handled;
566 }
567
window_has_map_scrolling(void)568 static int window_has_map_scrolling(void)
569 {
570 return window_is(WINDOW_CITY) ||
571 window_is(WINDOW_CITY_MILITARY) ||
572 window_is(WINDOW_EDITOR_MAP) ||
573 window_is(WINDOW_EMPIRE) ||
574 window_is(WINDOW_EDITOR_EMPIRE);
575 }
576
translate_window_scrolling(void)577 static int translate_window_scrolling(void)
578 {
579 if (window_has_map_scrolling()) {
580 return 0;
581 }
582 int handled = 0;
583 scroll_state current_scroll = SCROLL_NONE;
584 mapped_input scroll_up, scroll_down;
585
586 handled |= get_joystick_input_for_action(MAPPING_ACTION_SCROLL_WINDOW_UP, &scroll_up);
587 handled |= get_joystick_input_for_action(MAPPING_ACTION_SCROLL_WINDOW_DOWN, &scroll_down);
588
589 if (!handled) {
590 return 0;
591 }
592
593 // All mouse scrolling input will internally be treated as a joystick axis
594 translate_input_for_element(&scroll_up, JOYSTICK_ELEMENT_AXIS);
595 translate_input_for_element(&scroll_down, JOYSTICK_ELEMENT_AXIS);
596
597 unsigned int max_scroll_time = 50;
598
599 if (scroll_up.value) {
600 current_scroll = SCROLL_UP;
601 max_scroll_time = max_scroll_time * AXIS_MAX_VALUE / scroll_up.value;
602 } else if (scroll_down.value) {
603 current_scroll = SCROLL_DOWN;
604 max_scroll_time = max_scroll_time * AXIS_MAX_VALUE / scroll_down.value;
605 }
606
607 if (current_scroll != SCROLL_NONE) {
608 time_millis current_time = time_get_millis();
609 if (current_time - data.mouse.last_scroll_time > max_scroll_time) {
610 data.mouse.last_scroll_time = current_time;
611 mouse_set_scroll(current_scroll);
612 }
613 return 1;
614 } else {
615 data.mouse.last_scroll_time = 0;
616 return 0;
617 }
618 }
619
translate_map_scrolling(void)620 static int translate_map_scrolling(void)
621 {
622 if (!window_has_map_scrolling()) {
623 return 0;
624 }
625
626 int handled = get_joystick_input_for_action(MAPPING_ACTION_SCROLL_MAP_UP, &data.map_scroll[DIRECTION_UP]);
627 handled |= get_joystick_input_for_action(MAPPING_ACTION_SCROLL_MAP_LEFT, &data.map_scroll[DIRECTION_LEFT]);
628 handled |= get_joystick_input_for_action(MAPPING_ACTION_SCROLL_MAP_DOWN, &data.map_scroll[DIRECTION_DOWN]);
629 handled |= get_joystick_input_for_action(MAPPING_ACTION_SCROLL_MAP_RIGHT, &data.map_scroll[DIRECTION_RIGHT]);
630
631 if (!handled) {
632 int stopped_scrolling = 0;
633 for (int direction = 0; direction < NUM_DIRECTIONS; ++direction) {
634 if (data.map_scroll[direction].state == INPUT_STATE_IS_DOWN ||
635 data.map_scroll[direction].state == INPUT_STATE_WENT_DOWN) {
636 stopped_scrolling = 0;
637 break;
638 } else if (data.map_scroll[direction].state == INPUT_STATE_WENT_UP) {
639 stopped_scrolling |= 1;
640 }
641 }
642 if (stopped_scrolling) {
643 scroll_arrow_up(0);
644 scroll_arrow_left(0);
645 scroll_arrow_down(0);
646 scroll_arrow_right(0);
647 }
648 return 0;
649 }
650
651 joystick_element base_element = get_highest_priority_element(data.map_scroll, 4);
652
653 translate_input_for_element(&data.map_scroll[DIRECTION_UP], base_element);
654 translate_input_for_element(&data.map_scroll[DIRECTION_LEFT], base_element);
655 translate_input_for_element(&data.map_scroll[DIRECTION_DOWN], base_element);
656 translate_input_for_element(&data.map_scroll[DIRECTION_RIGHT], base_element);
657
658 if (base_element == JOYSTICK_ELEMENT_AXIS) {
659 rescale_axis(data.map_scroll);
660 }
661
662 scroll_arrow_up(data.map_scroll[DIRECTION_UP].value);
663 scroll_arrow_left(data.map_scroll[DIRECTION_LEFT].value);
664 scroll_arrow_down(data.map_scroll[DIRECTION_DOWN].value);
665 scroll_arrow_right(data.map_scroll[DIRECTION_RIGHT].value);
666
667 return 1;
668 }
669
translate_hotkeys(void)670 static int translate_hotkeys(void)
671 {
672 int handled = 0;
673 for (int i = 0; i < MAX_HOTKEYS; ++i) {
674 int value = get_joystick_input_for_action(HOTKEY_OFFSET + i, &data.joystick_hotkey[i]);
675 handled |= value;
676 if (value) {
677 if (data.joystick_hotkey[i].state == INPUT_STATE_WENT_DOWN) {
678 hotkey_set_value_for_action(JOYSTICK_MAPPING_TO_HOTKEY_ACTION[i], 1);
679 }
680 } else if (data.joystick_hotkey[i].state == INPUT_STATE_WENT_UP) {
681 hotkey_set_value_for_action(JOYSTICK_MAPPING_TO_HOTKEY_ACTION[i], 0);
682 }
683 }
684 return handled;
685 }
686
translate_system_functions(void)687 int translate_system_functions(void)
688 {
689 if (get_joystick_input_for_action(MAPPING_ACTION_SHOW_VIRTUAL_KEYBOARD, &data.virtual_keyboard)) {
690 if (data.virtual_keyboard.state == INPUT_STATE_WENT_DOWN) {
691 if (keyboard_is_capturing()) {
692 system_keyboard_show();
693 }
694 }
695 return 1;
696 }
697 if (get_joystick_input_for_action(MAPPING_ACTION_CYCLE_TOUCH_TYPE, &data.touch_mode)) {
698 if (data.touch_mode.state == INPUT_STATE_WENT_DOWN) {
699 touch_cycle_mode();
700 }
701 return 1;
702 }
703 return 0;
704 }
705
joystick_to_mouse_and_keyboard(void)706 int joystick_to_mouse_and_keyboard(void)
707 {
708 if (data.connected_joysticks == 0) {
709 return 0;
710 }
711 int handled = 0;
712 if (!translate_mapping_reset()) {
713 handled |= translate_mouse();
714 handled |= translate_window_scrolling();
715 handled |= translate_map_scrolling();
716 handled |= translate_hotkeys();
717 handled |= translate_system_functions();
718 }
719 for (int i = 0; i < MAX_CONTROLLERS; ++i) {
720 memset(data.joystick[i].trackball, 0, sizeof(joystick_trackball) * MAX_TRACKBALLS);
721 }
722 if (handled) {
723 mouse_remove_touch();
724 }
725 return handled;
726 }
727