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