1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 
5 #include <SDL.h>
6 #include <stdlib.h>
7 #include <fs/conf.h>
8 #include <fs/lazyness.h>
9 #include <fs/log.h>
10 #include <fs/glib.h>
11 #include <fs/ml/options.h>
12 #include "ml_internal.h"
13 
14 // For fs_emu_mouse_integration
15 #include <fs/emu.h>
16 
17 static GQueue *g_input_queue = NULL;
18 static fs_mutex *g_input_mutex = NULL;
19 static fs_ml_input_function g_input_function = NULL;
20 
21 int g_fs_log_input = 0;
22 int g_fs_ml_first_keyboard_index = 0;
23 int g_fs_ml_first_mouse_index = 0;
24 int g_fs_ml_first_joystick_index = 0;
25 
fs_ml_first_mouse_index(void)26 int fs_ml_first_mouse_index(void)
27 {
28     return g_fs_ml_first_mouse_index;
29 }
30 
31 /* maps SDL joystick indices to fs_ml indices */
32 SDL_JoystickID g_fs_ml_sdl_joystick_index_map[MAX_SDL_JOYSTICK_IDS];
33 
34 #define CURSOR_MODE_ON 1
35 #define CURSOR_MODE_OFF 0
36 #define CURSOR_MODE_AUTO -1
37 static int g_cursor_mode = CURSOR_MODE_ON;
38 
39 static int g_mouse_integration = 0;
40 
fs_ml_mouse_integration(void)41 bool fs_ml_mouse_integration(void)
42 {
43     return g_mouse_integration;
44 }
45 
fs_ml_cursor_allowed(void)46 bool fs_ml_cursor_allowed(void)
47 {
48     return g_cursor_mode != CURSOR_MODE_OFF;
49 }
50 
fs_ml_alloc_event()51 fs_ml_event* fs_ml_alloc_event()
52 {
53     return g_malloc(sizeof(fs_ml_event));
54 }
55 
fs_ml_input_event_free(fs_ml_event * event)56 static void fs_ml_input_event_free(fs_ml_event *event)
57 {
58     g_free(event);
59 }
60 
fs_ml_set_input_function(fs_ml_input_function function)61 void fs_ml_set_input_function(fs_ml_input_function function)
62 {
63     g_input_function = function;
64 }
65 
fs_ml_post_event(fs_ml_event * event)66 int fs_ml_post_event(fs_ml_event* event)
67 {
68     if (g_input_function) {
69         g_input_function(event);
70     }
71     return 1;
72 }
73 
fs_ml_input_fix_joystick_name(const char * name,int upper)74 char *fs_ml_input_fix_joystick_name(const char *name, int upper)
75 {
76     char *n, *temp;
77     n = g_strdup(name);
78     g_strstrip(n);
79 
80     if (n[0] == '\0') {
81         g_free(n);
82         n = g_strdup("Unnamed");
83     }
84 
85 #if 0
86 #warning Joystick device test hack enabled
87     if (strcasecmp(n, "Microsoft X-Box 360 pad") == 0) {
88         /* To test the above hacks. */
89         temp = n;
90         //n = g_strdup("Hack Test");
91         n = g_strdup("XInput Controller #1");
92         g_free(temp);
93     }
94 #endif
95 
96     if (strncasecmp(n, "XInput Controller #", 19) == 0) {
97         /* Hack because parts of the code don't like #x in the original
98          * joystick names. SDL 2 has begun naming XInput devices like this. */
99         temp = n;
100         n = g_strdup("XInput Controller");
101         g_free(temp);
102     }
103 
104     if (strcmp(n, "\xd0\x89") == 0) {
105         /* Hack for a specific joystick adapter having a single non-ASCII
106          * letter as device name. */
107         temp = n;
108         n = g_strdup("Hexagons Joystick Adapter");
109         g_free(temp);
110     }
111 
112     if (upper) {
113         temp = n;
114         n = g_ascii_strup(n, -1);
115         g_free(temp);
116     }
117     return n;
118 }
119 
fs_ml_check_joystick_blacklist_by_guid(const char * guid)120 static int fs_ml_check_joystick_blacklist_by_guid(const char *guid)
121 {
122     if (false) {
123 #if 0
124     } else if (strcasecmp(guid, "efbeedfe000000000000504944564944") == 0) {
125         /* VIRTUAL HID DEVICE */
126         return 1;
127 #endif
128 #if 0
129     } else if (strcasecmp(guid, "030000000b0400003365000000010000") == 0) {
130         /* MOSIC      SPEED-LINK COMPETITION PRO */
131         return 1;
132 #endif
133     }
134     return 0;
135 }
136 
fs_ml_input_list_custom_keyboards(void)137 GList *fs_ml_input_list_custom_keyboards(void)
138 {
139 #if 0
140     char *keyboards_path = g_build_filename(
141         fs_data_dir(), "Devs", NULL);
142 #else
143     char *keyboards_path = g_build_filename(
144         fs_data_dir(), "Devs", "Keyboards", NULL);
145 #endif
146     printf("# Keyboards path: %s\n", keyboards_path);
147     GList *list = NULL;
148     GDir *dir = g_dir_open(keyboards_path, 0, NULL);
149     if (dir) {
150         const gchar *name;
151         while ((name = g_dir_read_name(dir))) {
152             if (!g_str_has_suffix(name, ".ini")) {
153                 continue;
154             }
155             gchar *path = g_build_filename(keyboards_path, name, NULL);
156             GKeyFile *key_file = g_key_file_new();
157             if (!g_key_file_load_from_file(
158                     key_file, path, G_KEY_FILE_NONE, NULL)) {
159                 fs_log("WARNING: Could not load %s\n", path);
160                 continue;
161             }
162             gchar *type = g_key_file_get_string(
163                         key_file, "device", "type", NULL);
164             if (!type || strcmp(type, "keyboard") != 0) {
165                 g_free(type);
166                 continue;
167             }
168             g_free(type);
169             gchar *attached = g_key_file_get_string(
170                         key_file, "device", "attached", NULL);
171             if (!attached || strcmp(attached, "1") != 0) {
172                 g_free(attached);
173                 continue;
174             }
175             g_free(attached);
176             char *name2 = g_strdup(name);
177             name2[strlen(name) - 4] = '\0';
178             gchar *disable_name = g_strdup_printf("%s.disabled", name2);
179             gchar *disable_path = g_build_filename(
180                 keyboards_path, disable_name, NULL);
181             g_free(disable_name);
182             if (g_file_test(disable_path, G_FILE_TEST_EXISTS)) {
183                 g_free(disable_path);
184                 continue;
185             }
186             g_free(disable_path);
187             list = g_list_append(list, name2);
188         }
189         g_dir_close(dir);
190     }
191     list = g_list_sort(list, (GCompareFunc) g_ascii_strcasecmp);
192     gchar *disable_path = g_build_filename(
193                 keyboards_path, "Keyboard.disabled", NULL);
194     if (!g_file_test(disable_path, G_FILE_TEST_EXISTS)) {
195         list = g_list_prepend(list, g_strdup("Keyboard"));
196     }
197     g_free(disable_path);
198     g_free(keyboards_path);
199     return list;
200 }
201 
fs_ml_input_init()202 void fs_ml_input_init()
203 {
204     FS_ML_INIT_ONCE;
205 
206     SDL_Init(SDL_INIT_JOYSTICK);
207 
208     fs_log("[INPUT] fs_ml_input_init\n");
209 
210     g_fs_log_input = getenv("FS_DEBUG_INPUT") && \
211             getenv("FS_DEBUG_INPUT")[0] == '1';
212 
213     if (fs_config_get_boolean(OPTION_LOG_INPUT) == 1) {
214         fs_log("[INPUT] enable input event logging\n");
215         g_fs_log_input = 1;
216     }
217 
218     if (fs_config_get_boolean(OPTION_MOUSE_INTEGRATION) == 1) {
219         g_mouse_integration = 1;
220     }
221 
222     g_cursor_mode = fs_config_get_boolean(OPTION_CURSOR);
223     if (fs_config_check_auto(OPTION_CURSOR, FS_CONFIG_AUTO)) {
224         if (fs_emu_mouse_integration()) {
225             g_cursor_mode = CURSOR_MODE_OFF;
226         } else {
227             g_cursor_mode = CURSOR_MODE_AUTO;
228         }
229     }
230 
231     g_input_queue = g_queue_new();
232     g_input_mutex = fs_mutex_create();
233 
234     int size = sizeof(fs_ml_input_device) * FS_ML_INPUT_DEVICES_MAX;
235     /* Allocate zeroed memory. */
236     g_fs_ml_input_devices = g_malloc0(size);
237 
238     fs_ml_initialize_keymap();
239 
240     int k = 0;
241     g_fs_ml_first_joystick_index = 0;
242 #if 0
243     g_fs_ml_input_devices[k].type = FS_ML_KEYBOARD;
244     g_fs_ml_input_devices[k].index = k;
245     g_fs_ml_input_devices[k].name = g_strdup("KEYBOARD");
246     g_fs_ml_input_devices[k].alias = g_strdup("KEYBOARD");
247     k += 1;
248 #endif
249     GList *list = fs_ml_input_list_custom_keyboards();
250     GList *iterator = list;
251     while (iterator) {
252         const char *name = (const char *) iterator->data;
253         g_fs_ml_input_devices[k].type = FS_ML_KEYBOARD;
254         g_fs_ml_input_devices[k].index = k;
255         g_fs_ml_input_devices[k].name = g_strdup(name);
256         g_fs_ml_input_devices[k].alias = g_strdup(name);
257         k += 1;
258         iterator = g_list_next(iterator);
259     }
260     g_list_free_full(list, g_free);
261 
262     g_fs_ml_input_device_count = k;
263     fs_ml_mouse_init();
264     k = g_fs_ml_input_device_count;
265 
266     g_fs_ml_first_joystick_index = g_fs_ml_input_device_count;
267 
268     int num_joysticks = SDL_NumJoysticks();
269     fs_log("[INPUT] Joystick device count: %d\n", num_joysticks);
270     if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) {
271         fs_log("[INPUT] WARNING: Joystick module not initialized\n");
272     }
273     for (int i = 0; i < num_joysticks; i++) {
274         if (k == FS_ML_INPUT_DEVICES_MAX) {
275             fs_log("[INPUT] WARNING: Reached max num devices\n");
276             break;
277         }
278 
279         char guid_str[33];
280         SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(i);
281         SDL_JoystickGetGUIDString(guid, guid_str, 33);
282         guid_str[32] = '\0';
283         fs_log("[INPUT] SDL_JoystickGetDeviceGUID(%d) = %s\n", i, guid_str);
284         if (fs_ml_check_joystick_blacklist_by_guid(guid_str)) {
285             fs_log("[INPUT] Blacklisted joystick, not opening!\n");
286             continue;
287         }
288 
289         SDL_Joystick *joystick = SDL_JoystickOpen(i);
290         fs_log("[INPUT] SDL_JoystickOpen(%d)\n", i);
291 
292 #ifdef USE_SDL2
293         char *name = fs_ml_input_fix_joystick_name(
294             SDL_JoystickName(joystick), 1);
295 #else
296         char *name = fs_ml_input_fix_joystick_name(
297             SDL_JoystickName(i), 1);
298 #endif
299 
300         /* fs_ml_input_unique_device_name either returns name, or frees it
301          * and return another name, so name must be malloced and owned by
302          * caller. */
303         name = fs_ml_input_unique_device_name(name);
304 
305         g_fs_ml_input_devices[k].type = FS_ML_JOYSTICK;
306         g_fs_ml_input_devices[k].index = k;
307         g_fs_ml_input_devices[k].name = name;
308         if (i == 0) {
309             g_fs_ml_input_devices[k].alias = g_strdup("JOYSTICK");
310         } else {
311             g_fs_ml_input_devices[k].alias = g_strdup_printf("JOYSTICK #%d",
312                                                              i + 1);
313         }
314 
315         g_fs_ml_input_devices[k].hats = SDL_JoystickNumHats(joystick);
316         g_fs_ml_input_devices[k].buttons = SDL_JoystickNumButtons(joystick);
317         g_fs_ml_input_devices[k].axes = SDL_JoystickNumAxes(joystick);
318         g_fs_ml_input_devices[k].balls = SDL_JoystickNumBalls(joystick);
319 
320         fs_log("[INPUT] Joystick device #%02d found: %s\n", i + 1, name);
321         fs_log("[INPUT] %d buttons %d hats %d axes %d balls\n",
322                g_fs_ml_input_devices[k].buttons,
323                g_fs_ml_input_devices[k].hats,
324                g_fs_ml_input_devices[k].axes,
325                g_fs_ml_input_devices[k].balls);
326 
327         SDL_JoystickID instance_id = SDL_JoystickInstanceID(joystick);
328         fs_log("[INPUT] Joystick instance ID = %d\n", instance_id);
329         if (instance_id >= MAX_SDL_JOYSTICK_IDS) {
330             fs_log("[INPUT] SDL_JoystickID > %d\n", MAX_SDL_JOYSTICK_IDS);
331             fs_log("[INPUT] Closing joystick\n");
332             SDL_JoystickClose(joystick);
333             continue;
334         }
335 
336         g_fs_ml_sdl_joystick_index_map[instance_id] = k;
337         k += 1;
338     }
339 
340     g_fs_ml_input_device_count = k;
341 
342     fs_ml_initialize_keymap();
343 }
344