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