task_autodetect.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  1. /* RetroArch - A frontend for libretro.
  2. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
  3. * Copyright (C) 2011-2017 - Daniel De Matteis
  4. * Copyright (C) 2016-2019 - Brad Parker
  5. * Copyright (C) 2016-2019 - Andrés Suárez
  6. *
  7. * RetroArch is free software: you can redistribute it and/or modify it under the terms
  8. * of the GNU General Public License as published by the Free Software Found-
  9. * ation, either version 3 of the License, or (at your option) any later version.
  10. *
  11. * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  12. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  13. * PURPOSE. See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along with RetroArch.
  16. * If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <string.h>
  19. #include <stdlib.h>
  20. #include <ctype.h>
  21. #include <compat/strl.h>
  22. #include <file/file_path.h>
  23. #include <string/stdstring.h>
  24. #include <file/config_file.h>
  25. #include "../configuration.h"
  26. #include "../file_path_special.h"
  27. #include "../list_special.h"
  28. #include "../verbosity.h"
  29. #include "../input/input_driver.h"
  30. #include "../input/input_remapping.h"
  31. #include "tasks_internal.h"
  32. #ifdef HAVE_BLISSBOX
  33. #include "../input/include/blissbox.h"
  34. #endif
  35. #ifdef HAVE_MENU
  36. #include "../menu/menu_driver.h"
  37. #endif
  38. enum autoconfig_handle_flags
  39. {
  40. AUTOCONF_FLAG_AUTOCONFIG_ENABLED = (1 << 0),
  41. AUTOCONF_FLAG_SUPPRESS_NOTIFICATIONS = (1 << 1)
  42. };
  43. typedef struct
  44. {
  45. char *dir_autoconfig;
  46. char *dir_driver_autoconfig;
  47. config_file_t *autoconfig_file;
  48. unsigned port;
  49. input_device_info_t device_info; /* unsigned alignment */
  50. uint8_t flags;
  51. } autoconfig_handle_t;
  52. /*********************/
  53. /* Utility functions */
  54. /*********************/
  55. static void free_autoconfig_handle(autoconfig_handle_t *autoconfig_handle)
  56. {
  57. if (!autoconfig_handle)
  58. return;
  59. if (autoconfig_handle->dir_autoconfig)
  60. {
  61. free(autoconfig_handle->dir_autoconfig);
  62. autoconfig_handle->dir_autoconfig = NULL;
  63. }
  64. if (autoconfig_handle->dir_driver_autoconfig)
  65. {
  66. free(autoconfig_handle->dir_driver_autoconfig);
  67. autoconfig_handle->dir_driver_autoconfig = NULL;
  68. }
  69. if (autoconfig_handle->autoconfig_file)
  70. {
  71. config_file_free(autoconfig_handle->autoconfig_file);
  72. autoconfig_handle->autoconfig_file = NULL;
  73. }
  74. free(autoconfig_handle);
  75. autoconfig_handle = NULL;
  76. }
  77. static void input_autoconfigure_free(retro_task_t *task)
  78. {
  79. autoconfig_handle_t *autoconfig_handle = NULL;
  80. if (task && (autoconfig_handle = (autoconfig_handle_t*)task->state))
  81. free_autoconfig_handle(autoconfig_handle);
  82. }
  83. /******************************/
  84. /* Autoconfig 'File' Handling */
  85. /******************************/
  86. /* Returns a value corresponding to the
  87. * 'affinity' between the connected input
  88. * device and the specified config file
  89. * > 0: No match
  90. * > 2: Device name matches
  91. * > 3: VID+PID match
  92. * > 5: Both device name and VID+PID match */
  93. static unsigned input_autoconfigure_get_config_file_affinity(
  94. autoconfig_handle_t *autoconfig_handle,
  95. config_file_t *config)
  96. {
  97. int tmp_int = 0;
  98. uint16_t config_vid = 0;
  99. uint16_t config_pid = 0;
  100. bool pid_match = false;
  101. unsigned affinity = 0;
  102. struct config_entry_list
  103. *entry = NULL;
  104. /* Parse config file */
  105. if (config_get_int(config, "input_vendor_id", &tmp_int))
  106. config_vid = (uint16_t)tmp_int;
  107. if (config_get_int(config, "input_product_id", &tmp_int))
  108. config_pid = (uint16_t)tmp_int;
  109. /* > Bliss-Box shenanigans... */
  110. #ifdef HAVE_BLISSBOX
  111. if (autoconfig_handle->device_info.vid == BLISSBOX_VID)
  112. config_pid = BLISSBOX_PID;
  113. #endif
  114. /* Check for matching VID+PID */
  115. pid_match = (autoconfig_handle->device_info.vid == config_vid) &&
  116. (autoconfig_handle->device_info.pid == config_pid) &&
  117. (autoconfig_handle->device_info.vid != 0) &&
  118. (autoconfig_handle->device_info.pid != 0);
  119. /* > More Bliss-Box shenanigans... */
  120. #ifdef HAVE_BLISSBOX
  121. pid_match = pid_match &&
  122. (autoconfig_handle->device_info.vid != BLISSBOX_VID) &&
  123. (autoconfig_handle->device_info.pid != BLISSBOX_PID);
  124. #endif
  125. if (pid_match)
  126. affinity += 3;
  127. /* Check for matching device name */
  128. if ( (entry = config_get_entry(config, "input_device"))
  129. && !string_is_empty(entry->value)
  130. && string_is_equal(entry->value,
  131. autoconfig_handle->device_info.name))
  132. affinity += 2;
  133. return affinity;
  134. }
  135. /* 'Attaches' specified autoconfig file to autoconfig
  136. * handle, parsing required device info metadata */
  137. static void input_autoconfigure_set_config_file(
  138. autoconfig_handle_t *autoconfig_handle,
  139. config_file_t *config)
  140. {
  141. struct config_entry_list *entry = NULL;
  142. /* Attach config file */
  143. autoconfig_handle->autoconfig_file = config;
  144. /* > Extract config file path + name */
  145. if (!string_is_empty(config->path))
  146. {
  147. const char *config_file_name = path_basename_nocompression(config->path);
  148. strlcpy(autoconfig_handle->device_info.config_path,
  149. config->path,
  150. sizeof(autoconfig_handle->device_info.config_path));
  151. if (!string_is_empty(config_file_name))
  152. strlcpy(autoconfig_handle->device_info.config_name,
  153. config_file_name,
  154. sizeof(autoconfig_handle->device_info.config_name));
  155. }
  156. /* Read device display name */
  157. if ( (entry = config_get_entry(config, "input_device_display_name"))
  158. && !string_is_empty(entry->value))
  159. strlcpy(autoconfig_handle->device_info.display_name,
  160. entry->value,
  161. sizeof(autoconfig_handle->device_info.display_name));
  162. /* Set auto-configured status to 'true' */
  163. autoconfig_handle->device_info.autoconfigured = true;
  164. }
  165. /* Attempts to find an 'external' autoconfig file
  166. * (in the autoconfig directory) matching the connected
  167. * input device
  168. * > Returns 'true' if successful */
  169. static bool input_autoconfigure_scan_config_files_external(
  170. autoconfig_handle_t *autoconfig_handle)
  171. {
  172. size_t i;
  173. const char *dir_autoconfig = autoconfig_handle->dir_autoconfig;
  174. const char *dir_driver_autoconfig = autoconfig_handle->dir_driver_autoconfig;
  175. struct string_list *config_file_list = NULL;
  176. config_file_t *best_config = NULL;
  177. unsigned max_affinity = 0;
  178. bool match_found = false;
  179. /* Attempt to fetch file listing from driver-specific
  180. * autoconfig directory */
  181. if (!string_is_empty(dir_driver_autoconfig) &&
  182. path_is_directory(dir_driver_autoconfig))
  183. config_file_list = dir_list_new_special(
  184. dir_driver_autoconfig, DIR_LIST_AUTOCONFIG,
  185. "cfg", false);
  186. if (!config_file_list || (config_file_list->size < 1))
  187. {
  188. /* No files found - attempt to fetch listing
  189. * from autoconfig base directory */
  190. if (config_file_list)
  191. {
  192. string_list_free(config_file_list);
  193. config_file_list = NULL;
  194. }
  195. if (!string_is_empty(dir_autoconfig) &&
  196. path_is_directory(dir_autoconfig))
  197. config_file_list = dir_list_new_special(
  198. dir_autoconfig, DIR_LIST_AUTOCONFIG,
  199. "cfg", false);
  200. }
  201. if (!config_file_list || (config_file_list->size < 1))
  202. goto end;
  203. /* Loop through external config files */
  204. for (i = 0; i < config_file_list->size; i++)
  205. {
  206. const char *config_file_path = config_file_list->elems[i].data;
  207. config_file_t *config = NULL;
  208. unsigned affinity = 0;
  209. if (string_is_empty(config_file_path))
  210. continue;
  211. /* Load autoconfig file */
  212. if (!(config = config_file_new_from_path_to_string(config_file_path)))
  213. continue;
  214. /* Check for a match */
  215. if (autoconfig_handle && config)
  216. affinity = input_autoconfigure_get_config_file_affinity(
  217. autoconfig_handle, config);
  218. if (affinity > max_affinity)
  219. {
  220. if (best_config)
  221. {
  222. config_file_free(best_config);
  223. best_config = NULL;
  224. }
  225. /* 'Cache' config file for later processing */
  226. best_config = config;
  227. config = NULL;
  228. max_affinity = affinity;
  229. /* An affinity of 5 is a 'perfect' match,
  230. * and means we can return immediately */
  231. if (affinity == 5)
  232. break;
  233. }
  234. /* No match - just clean up config file */
  235. else
  236. {
  237. config_file_free(config);
  238. config = NULL;
  239. }
  240. }
  241. /* If we reach this point and a config file has
  242. * been cached, then we have a match */
  243. if (best_config)
  244. {
  245. if (autoconfig_handle && best_config)
  246. input_autoconfigure_set_config_file(
  247. autoconfig_handle, best_config);
  248. match_found = true;
  249. }
  250. end:
  251. if (config_file_list)
  252. {
  253. string_list_free(config_file_list);
  254. config_file_list = NULL;
  255. }
  256. return match_found;
  257. }
  258. /* Attempts to find an internal autoconfig definition
  259. * matching the connected input device
  260. * > Returns 'true' if successful */
  261. static bool input_autoconfigure_scan_config_files_internal(
  262. autoconfig_handle_t *autoconfig_handle)
  263. {
  264. size_t i;
  265. /* Loop through internal autoconfig files
  266. * > input_builtin_autoconfs is a static const,
  267. * and may be read safely in any thread */
  268. for (i = 0; input_builtin_autoconfs[i]; i++)
  269. {
  270. char *autoconfig_str = NULL;
  271. config_file_t *config = NULL;
  272. unsigned affinity = 0;
  273. if (string_is_empty(input_builtin_autoconfs[i]))
  274. continue;
  275. /* Load autoconfig string */
  276. autoconfig_str = strdup(input_builtin_autoconfs[i]);
  277. config = config_file_new_from_string(
  278. autoconfig_str, NULL);
  279. /* > String no longer required - clean up */
  280. free(autoconfig_str);
  281. autoconfig_str = NULL;
  282. /* Check for a match */
  283. if (autoconfig_handle && config)
  284. affinity = input_autoconfigure_get_config_file_affinity(
  285. autoconfig_handle, config);
  286. /* > In the case of internal autoconfigs, any kind
  287. * of match is considered to be a success */
  288. if (affinity > 0)
  289. {
  290. if (autoconfig_handle && config)
  291. input_autoconfigure_set_config_file(
  292. autoconfig_handle, config);
  293. return true;
  294. }
  295. /* No match - clean up */
  296. if (config)
  297. {
  298. config_file_free(config);
  299. config = NULL;
  300. }
  301. }
  302. return false;
  303. }
  304. /*************************/
  305. /* Autoconfigure Connect */
  306. /*************************/
  307. static void cb_input_autoconfigure_connect(
  308. retro_task_t *task, void *task_data,
  309. void *user_data, const char *err)
  310. {
  311. autoconfig_handle_t *autoconfig_handle = NULL;
  312. unsigned port;
  313. if (!task)
  314. return;
  315. if (!(autoconfig_handle = (autoconfig_handle_t*)task->state))
  316. return;
  317. /* Use local copy of port index for brevity... */
  318. port = autoconfig_handle->port;
  319. /* We perform the actual 'connect' in this
  320. * callback, to ensure it occurs on the main
  321. * thread */
  322. /* Copy task handle parameters into global
  323. * state objects:
  324. * > Name */
  325. if (!string_is_empty(autoconfig_handle->device_info.name))
  326. input_config_set_device_name(port,
  327. autoconfig_handle->device_info.name);
  328. else
  329. input_config_clear_device_name(port);
  330. /* > Display name */
  331. if (!string_is_empty(autoconfig_handle->device_info.display_name))
  332. input_config_set_device_display_name(port,
  333. autoconfig_handle->device_info.display_name);
  334. else if (!string_is_empty(autoconfig_handle->device_info.name))
  335. input_config_set_device_display_name(port,
  336. autoconfig_handle->device_info.name);
  337. else
  338. input_config_clear_device_display_name(port);
  339. /* > Driver */
  340. if (!string_is_empty(autoconfig_handle->device_info.joypad_driver))
  341. input_config_set_device_joypad_driver(port,
  342. autoconfig_handle->device_info.joypad_driver);
  343. else
  344. input_config_clear_device_joypad_driver(port);
  345. /* > VID/PID */
  346. input_config_set_device_vid(port, autoconfig_handle->device_info.vid);
  347. input_config_set_device_pid(port, autoconfig_handle->device_info.pid);
  348. /* > Config file path/name */
  349. if (!string_is_empty(autoconfig_handle->device_info.config_path))
  350. input_config_set_device_config_path(port,
  351. autoconfig_handle->device_info.config_path);
  352. else
  353. input_config_set_device_config_path(port,
  354. msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE));
  355. if (!string_is_empty(autoconfig_handle->device_info.config_name))
  356. input_config_set_device_config_name(port,
  357. autoconfig_handle->device_info.config_name);
  358. else
  359. input_config_set_device_config_name(port,
  360. msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE));
  361. /* > Auto-configured state */
  362. input_config_set_device_autoconfigured(port,
  363. autoconfig_handle->device_info.autoconfigured);
  364. /* Reset any existing binds */
  365. input_config_reset_autoconfig_binds(port);
  366. /* If an autoconfig file is available, load its
  367. * bind mappings */
  368. if (autoconfig_handle->device_info.autoconfigured)
  369. input_config_set_autoconfig_binds(port,
  370. autoconfig_handle->autoconfig_file);
  371. }
  372. static void input_autoconfigure_connect_handler(retro_task_t *task)
  373. {
  374. autoconfig_handle_t *autoconfig_handle = NULL;
  375. bool match_found = false;
  376. const char *device_display_name = NULL;
  377. char task_title[NAME_MAX_LENGTH + 16];
  378. task_title[0] = '\0';
  379. if (!task)
  380. goto task_finished;
  381. autoconfig_handle = (autoconfig_handle_t*)task->state;
  382. if ( !autoconfig_handle
  383. || string_is_empty(autoconfig_handle->device_info.name)
  384. || !(autoconfig_handle->flags & AUTOCONF_FLAG_AUTOCONFIG_ENABLED))
  385. goto task_finished;
  386. /* Annoyingly, we have to scan all the autoconfig
  387. * files (and in-built configs) in a single shot
  388. * > Would prefer to scan one config per iteration
  389. * of the task, but this would render the gamepad
  390. * unusable for multiple frames after loading
  391. * content... */
  392. /* Scan in order of preference:
  393. * - External autoconfig files
  394. * - Internal autoconfig definitions */
  395. if (!(match_found = input_autoconfigure_scan_config_files_external(
  396. autoconfig_handle)))
  397. match_found = input_autoconfigure_scan_config_files_internal(
  398. autoconfig_handle);
  399. /* If no match was found, attempt to use
  400. * fallback mapping
  401. * > Only enabled for certain drivers */
  402. if (!match_found)
  403. {
  404. const char *fallback_device_name = NULL;
  405. /* Preset fallback device names - must match
  406. * those set in 'input_autodetect_builtin.c' */
  407. if (string_is_equal(autoconfig_handle->device_info.joypad_driver,
  408. "android"))
  409. fallback_device_name = "Android Gamepad";
  410. else if (string_is_equal(autoconfig_handle->device_info.joypad_driver,
  411. "xinput"))
  412. fallback_device_name = "XInput Controller";
  413. else if (string_is_equal(autoconfig_handle->device_info.joypad_driver,
  414. "sdl2"))
  415. fallback_device_name = "Standard Gamepad";
  416. if (!string_is_empty(fallback_device_name) &&
  417. !string_is_equal(autoconfig_handle->device_info.name,
  418. fallback_device_name))
  419. {
  420. char *name_backup = strdup(autoconfig_handle->device_info.name);
  421. strlcpy(autoconfig_handle->device_info.name,
  422. fallback_device_name,
  423. sizeof(autoconfig_handle->device_info.name));
  424. /* This is not a genuine match - leave
  425. * match_found set to 'false' regardless
  426. * of the outcome */
  427. input_autoconfigure_scan_config_files_internal(
  428. autoconfig_handle);
  429. strlcpy(autoconfig_handle->device_info.name,
  430. name_backup,
  431. sizeof(autoconfig_handle->device_info.name));
  432. free(name_backup);
  433. name_backup = NULL;
  434. }
  435. }
  436. /* Get display name for task status message */
  437. device_display_name = autoconfig_handle->device_info.display_name;
  438. if (string_is_empty(device_display_name))
  439. device_display_name = autoconfig_handle->device_info.name;
  440. if (string_is_empty(device_display_name))
  441. device_display_name = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE);
  442. /* Generate task status message
  443. * > Note that 'connection successful' messages
  444. * may be suppressed, but error messages are
  445. * always shown */
  446. if (autoconfig_handle->device_info.autoconfigured)
  447. {
  448. if (match_found)
  449. {
  450. /* A valid autoconfig was applied */
  451. if (!(autoconfig_handle->flags & AUTOCONF_FLAG_SUPPRESS_NOTIFICATIONS))
  452. snprintf(task_title, sizeof(task_title),
  453. "%s configured in port %u",
  454. device_display_name,
  455. autoconfig_handle->port + 1);
  456. }
  457. /* Device is autoconfigured, but a (most likely
  458. * incorrect) fallback definition was used... */
  459. else
  460. snprintf(task_title, sizeof(task_title),
  461. "%s (%u/%u) not configured, using fallback",
  462. device_display_name,
  463. autoconfig_handle->device_info.vid,
  464. autoconfig_handle->device_info.pid);
  465. }
  466. /* Autoconfig failed */
  467. else
  468. snprintf(task_title, sizeof(task_title),
  469. "%s (%u/%u) not configured",
  470. device_display_name,
  471. autoconfig_handle->device_info.vid,
  472. autoconfig_handle->device_info.pid);
  473. /* Update task title */
  474. task_free_title(task);
  475. if (!string_is_empty(task_title))
  476. {
  477. task_set_title(task, strdup(task_title));
  478. RARCH_LOG("[Autoconf]: %s.\n", task_title);
  479. }
  480. task_finished:
  481. if (task)
  482. task_set_finished(task, true);
  483. }
  484. static bool autoconfigure_connect_finder(retro_task_t *task, void *user_data)
  485. {
  486. autoconfig_handle_t *autoconfig_handle = NULL;
  487. unsigned *port = NULL;
  488. if (!task || !user_data)
  489. return false;
  490. if (task->handler != input_autoconfigure_connect_handler)
  491. return false;
  492. autoconfig_handle = (autoconfig_handle_t*)task->state;
  493. if (!autoconfig_handle)
  494. return false;
  495. port = (unsigned*)user_data;
  496. return (*port == autoconfig_handle->port);
  497. }
  498. bool input_autoconfigure_connect(
  499. const char *name,
  500. const char *display_name,
  501. const char *driver,
  502. unsigned port,
  503. unsigned vid,
  504. unsigned pid)
  505. {
  506. retro_task_t *task = NULL;
  507. autoconfig_handle_t *autoconfig_handle = NULL;
  508. bool driver_valid = false;
  509. settings_t *settings = config_get_ptr();
  510. bool autoconfig_enabled = settings ?
  511. settings->bools.input_autodetect_enable : false;
  512. const char *dir_autoconfig = settings ?
  513. settings->paths.directory_autoconfig : NULL;
  514. bool notification_show_autoconfig = settings ?
  515. settings->bools.notification_show_autoconfig : true;
  516. task_finder_data_t find_data;
  517. if (port >= MAX_INPUT_DEVICES)
  518. goto error;
  519. /* Cannot connect a device that is currently
  520. * being connected */
  521. find_data.func = autoconfigure_connect_finder;
  522. find_data.userdata = (void*)&port;
  523. if (task_queue_find(&find_data))
  524. goto error;
  525. /* Configure handle */
  526. if (!(autoconfig_handle = (autoconfig_handle_t*)
  527. malloc(sizeof(autoconfig_handle_t))))
  528. goto error;
  529. autoconfig_handle->port = port;
  530. autoconfig_handle->device_info.vid = vid;
  531. autoconfig_handle->device_info.pid = pid;
  532. autoconfig_handle->device_info.name[0] = '\0';
  533. autoconfig_handle->device_info.display_name[0] = '\0';
  534. autoconfig_handle->device_info.config_path[0] = '\0';
  535. autoconfig_handle->device_info.config_name[0] = '\0';
  536. autoconfig_handle->device_info.joypad_driver[0] = '\0';
  537. autoconfig_handle->device_info.autoconfigured = false;
  538. autoconfig_handle->device_info.name_index = 0;
  539. if (autoconfig_enabled)
  540. autoconfig_handle->flags |= AUTOCONF_FLAG_AUTOCONFIG_ENABLED;
  541. if (!notification_show_autoconfig)
  542. autoconfig_handle->flags |= AUTOCONF_FLAG_SUPPRESS_NOTIFICATIONS;
  543. autoconfig_handle->dir_autoconfig = NULL;
  544. autoconfig_handle->dir_driver_autoconfig = NULL;
  545. autoconfig_handle->autoconfig_file = NULL;
  546. if (!string_is_empty(name))
  547. strlcpy(autoconfig_handle->device_info.name, name,
  548. sizeof(autoconfig_handle->device_info.name));
  549. if (!string_is_empty(display_name))
  550. strlcpy(autoconfig_handle->device_info.display_name, display_name,
  551. sizeof(autoconfig_handle->device_info.display_name));
  552. if ((driver_valid = !string_is_empty(driver)))
  553. strlcpy(autoconfig_handle->device_info.joypad_driver,
  554. driver, sizeof(autoconfig_handle->device_info.joypad_driver));
  555. /* > Have to cache both the base autoconfig directory
  556. * and the driver-specific autoconfig directory
  557. * - Driver-specific directory is scanned by
  558. * default, if available
  559. * - If driver-specific directory is unavailable,
  560. * we scan the base autoconfig directory as
  561. * a fallback */
  562. if (!string_is_empty(dir_autoconfig))
  563. {
  564. autoconfig_handle->dir_autoconfig = strdup(dir_autoconfig);
  565. if (driver_valid)
  566. {
  567. char dir_driver_autoconfig[PATH_MAX_LENGTH];
  568. /* Generate driver-specific autoconfig directory */
  569. fill_pathname_join_special(dir_driver_autoconfig,
  570. dir_autoconfig,
  571. autoconfig_handle->device_info.joypad_driver,
  572. sizeof(dir_driver_autoconfig));
  573. if (!string_is_empty(dir_driver_autoconfig))
  574. autoconfig_handle->dir_driver_autoconfig =
  575. strdup(dir_driver_autoconfig);
  576. }
  577. }
  578. /* Bliss-Box shenanigans... */
  579. #ifdef HAVE_BLISSBOX
  580. if (autoconfig_handle->device_info.vid == BLISSBOX_VID)
  581. input_autoconfigure_blissbox_override_handler(
  582. (int)autoconfig_handle->device_info.vid,
  583. (int)autoconfig_handle->device_info.pid,
  584. autoconfig_handle->device_info.name,
  585. sizeof(autoconfig_handle->device_info.name));
  586. #endif
  587. /* If we are reconnecting a device that is already
  588. * connected and autoconfigured, then there is no need
  589. * to generate additional 'connection successful'
  590. * task status messages
  591. * > Can skip this check if autoconfig notifications
  592. * have been disabled by the user */
  593. if ( !(autoconfig_handle->flags & AUTOCONF_FLAG_SUPPRESS_NOTIFICATIONS)
  594. && !string_is_empty(autoconfig_handle->device_info.name))
  595. {
  596. const char *last_device_name = input_config_get_device_name(port);
  597. uint16_t last_vid = input_config_get_device_vid(port);
  598. uint16_t last_pid = input_config_get_device_pid(port);
  599. bool last_autoconfigured = input_config_get_device_autoconfigured(port);
  600. if (!string_is_empty(last_device_name) &&
  601. string_is_equal(autoconfig_handle->device_info.name,
  602. last_device_name) &&
  603. (autoconfig_handle->device_info.vid == last_vid) &&
  604. (autoconfig_handle->device_info.pid == last_pid) &&
  605. last_autoconfigured)
  606. autoconfig_handle->flags |= AUTOCONF_FLAG_SUPPRESS_NOTIFICATIONS;
  607. }
  608. /* Configure task */
  609. task = task_init();
  610. if (!task)
  611. goto error;
  612. task->handler = input_autoconfigure_connect_handler;
  613. task->state = autoconfig_handle;
  614. task->mute = false;
  615. task->title = NULL;
  616. task->callback = cb_input_autoconfigure_connect;
  617. task->cleanup = input_autoconfigure_free;
  618. task_queue_push(task);
  619. return true;
  620. error:
  621. if (task)
  622. {
  623. free(task);
  624. task = NULL;
  625. }
  626. free_autoconfig_handle(autoconfig_handle);
  627. return false;
  628. }
  629. /****************************/
  630. /* Autoconfigure Disconnect */
  631. /****************************/
  632. static void cb_input_autoconfigure_disconnect(
  633. retro_task_t *task, void *task_data,
  634. void *user_data, const char *err)
  635. {
  636. unsigned port;
  637. autoconfig_handle_t *autoconfig_handle = NULL;
  638. if (!task)
  639. return;
  640. if (!(autoconfig_handle = (autoconfig_handle_t*)task->state))
  641. return;
  642. /* Use local copy of port index for brevity... */
  643. port = autoconfig_handle->port;
  644. /* We perform the actual 'disconnect' in this
  645. * callback, to ensure it occurs on the main thread */
  646. input_config_clear_device_name(port);
  647. input_config_clear_device_display_name(port);
  648. input_config_clear_device_config_path(port);
  649. input_config_clear_device_config_name(port);
  650. input_config_clear_device_joypad_driver(port);
  651. input_config_set_device_vid(port, 0);
  652. input_config_set_device_pid(port, 0);
  653. input_config_set_device_autoconfigured(port, false);
  654. input_config_reset_autoconfig_binds(port);
  655. }
  656. static void input_autoconfigure_disconnect_handler(retro_task_t *task)
  657. {
  658. autoconfig_handle_t *autoconfig_handle = NULL;
  659. const char *device_display_name = NULL;
  660. char task_title[NAME_MAX_LENGTH + 16];
  661. task_title[0] = '\0';
  662. if (!task)
  663. goto task_finished;
  664. if (!(autoconfig_handle = (autoconfig_handle_t*)task->state))
  665. goto task_finished;
  666. /* Get display name for task status message */
  667. device_display_name = autoconfig_handle->device_info.display_name;
  668. if (string_is_empty(device_display_name))
  669. device_display_name = autoconfig_handle->device_info.name;
  670. if (string_is_empty(device_display_name))
  671. device_display_name = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE);
  672. /* Set task title */
  673. snprintf(task_title, sizeof(task_title),
  674. msg_hash_to_str(MSG_DEVICE_DISCONNECTED_FROM_PORT_NR),
  675. device_display_name,
  676. autoconfig_handle->port + 1);
  677. task_free_title(task);
  678. if (!(autoconfig_handle->flags & AUTOCONF_FLAG_SUPPRESS_NOTIFICATIONS))
  679. task_set_title(task, strdup(task_title));
  680. if (!string_is_empty(task_title))
  681. RARCH_LOG("[Autoconf]: %s.\n", task_title);
  682. task_finished:
  683. if (task)
  684. task_set_finished(task, true);
  685. }
  686. static bool autoconfigure_disconnect_finder(retro_task_t *task, void *user_data)
  687. {
  688. autoconfig_handle_t *autoconfig_handle = NULL;
  689. unsigned *port = NULL;
  690. if (!task || !user_data)
  691. return false;
  692. if (task->handler != input_autoconfigure_disconnect_handler)
  693. return false;
  694. if (!(autoconfig_handle = (autoconfig_handle_t*)task->state))
  695. return false;
  696. port = (unsigned*)user_data;
  697. return (*port == autoconfig_handle->port);
  698. }
  699. /* Note: There is no real need for autoconfigure
  700. * 'disconnect' to be a task - we are merely setting
  701. * a handful of variables. However:
  702. * - Making it a task means we can call
  703. * input_autoconfigure_disconnect() on any thread
  704. * thread, and defer the global state changes until
  705. * the task queue is handled on the *main* thread
  706. * - By using a task for both 'connect' and 'disconnect',
  707. * we ensure uniformity of OSD status messages */
  708. bool input_autoconfigure_disconnect(unsigned port, const char *name)
  709. {
  710. retro_task_t *task = NULL;
  711. autoconfig_handle_t *autoconfig_handle = NULL;
  712. task_finder_data_t find_data;
  713. settings_t *settings = config_get_ptr();
  714. input_driver_state_t *input_st = input_state_get_ptr();
  715. bool notification_show_autoconfig = settings ? settings->bools.notification_show_autoconfig : true;
  716. bool pause_on_disconnect = settings ? settings->bools.pause_on_disconnect : true;
  717. bool core_is_running = runloop_state_get_ptr()->flags & RUNLOOP_FLAG_CORE_RUNNING;
  718. if (port >= MAX_INPUT_DEVICES)
  719. goto error;
  720. /* Cannot disconnect a device that is currently
  721. * being disconnected */
  722. find_data.func = autoconfigure_disconnect_finder;
  723. find_data.userdata = (void*)&port;
  724. if (task_queue_find(&find_data))
  725. goto error;
  726. /* Configure handle */
  727. autoconfig_handle = (autoconfig_handle_t*)calloc(1, sizeof(autoconfig_handle_t));
  728. if (!autoconfig_handle)
  729. goto error;
  730. autoconfig_handle->port = port;
  731. if (!notification_show_autoconfig)
  732. autoconfig_handle->flags |= AUTOCONF_FLAG_SUPPRESS_NOTIFICATIONS;
  733. /* Use display_name as name instead since autoconfig display_name
  734. * is destroyed already, and real name does not matter at this point */
  735. if (input_st && !string_is_empty(input_st->input_device_info[port].display_name))
  736. strlcpy(autoconfig_handle->device_info.name,
  737. input_st->input_device_info[port].display_name,
  738. sizeof(autoconfig_handle->device_info.name));
  739. else if (!string_is_empty(name))
  740. strlcpy(autoconfig_handle->device_info.name,
  741. name, sizeof(autoconfig_handle->device_info.name));
  742. /* Configure task */
  743. if (!(task = task_init()))
  744. goto error;
  745. task->handler = input_autoconfigure_disconnect_handler;
  746. task->state = autoconfig_handle;
  747. task->title = NULL;
  748. task->callback = cb_input_autoconfigure_disconnect;
  749. task->cleanup = input_autoconfigure_free;
  750. task_queue_push(task);
  751. if (pause_on_disconnect && core_is_running)
  752. {
  753. #ifdef HAVE_MENU
  754. bool menu_pause_libretro = settings->bools.menu_pause_libretro;
  755. bool menu_is_alive = menu_state_get_ptr()->flags & MENU_ST_FLAG_ALIVE;
  756. if (menu_pause_libretro && !menu_is_alive)
  757. command_event(CMD_EVENT_MENU_TOGGLE, NULL);
  758. else if (!menu_pause_libretro)
  759. command_event(CMD_EVENT_PAUSE, NULL);
  760. #else
  761. command_event(CMD_EVENT_PAUSE, NULL);
  762. #endif
  763. }
  764. return true;
  765. error:
  766. if (task)
  767. {
  768. free(task);
  769. task = NULL;
  770. }
  771. free_autoconfig_handle(autoconfig_handle);
  772. return false;
  773. }