obs-nix-x11.c 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270
  1. /******************************************************************************
  2. Copyright (C) 2013 by Hugh Bailey <[email protected]>
  3. Copyright (C) 2014 by Zachary Lund <[email protected]>
  4. Copyright (C) 2019 by Jason Francis <[email protected]>
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ******************************************************************************/
  16. #include "obs-internal.h"
  17. #include "obs-nix-x11.h"
  18. #include <xcb/xcb.h>
  19. #if USE_XINPUT
  20. #include <xcb/xinput.h>
  21. #endif
  22. #include <X11/Xlib.h>
  23. #include <X11/Xutil.h>
  24. #include <X11/Xlib-xcb.h>
  25. #include <X11/XF86keysym.h>
  26. #include <X11/Sunkeysym.h>
  27. void obs_nix_x11_log_info(void)
  28. {
  29. Display *dpy = XOpenDisplay(NULL);
  30. if (!dpy) {
  31. blog(LOG_INFO, "Unable to open X display");
  32. return;
  33. }
  34. int protocol_version = ProtocolVersion(dpy);
  35. int protocol_revision = ProtocolRevision(dpy);
  36. int vendor_release = VendorRelease(dpy);
  37. const char *vendor_name = ServerVendor(dpy);
  38. if (strstr(vendor_name, "X.Org")) {
  39. blog(LOG_INFO,
  40. "Window System: X%d.%d, Vendor: %s, Version: %d"
  41. ".%d.%d",
  42. protocol_version, protocol_revision, vendor_name,
  43. vendor_release / 10000000, (vendor_release / 100000) % 100,
  44. (vendor_release / 1000) % 100);
  45. } else {
  46. blog(LOG_INFO,
  47. "Window System: X%d.%d - vendor string: %s - "
  48. "vendor release: %d",
  49. protocol_version, protocol_revision, vendor_name,
  50. vendor_release);
  51. }
  52. }
  53. /* So here's how linux works with key mapping:
  54. *
  55. * First, there's a global key symbol enum (xcb_keysym_t) which has unique
  56. * values for all possible symbols keys can have (e.g., '1' and '!' are
  57. * different values).
  58. *
  59. * Then there's a key code (xcb_keycode_t), which is basically an index to the
  60. * actual key itself on the keyboard (e.g., '1' and '!' will share the same
  61. * value).
  62. *
  63. * xcb_keysym_t values should be given to libobs, and libobs will translate it
  64. * to an obs_key_t, and although xcb_keysym_t can differ ('!' vs '1'), it will
  65. * get the obs_key_t value that represents the actual key pressed; in other
  66. * words it will be based on the key code rather than the key symbol. The same
  67. * applies to checking key press states.
  68. */
  69. struct keycode_list {
  70. DARRAY(xcb_keycode_t) list;
  71. };
  72. struct obs_hotkeys_platform {
  73. Display *display;
  74. xcb_keysym_t base_keysyms[OBS_KEY_LAST_VALUE];
  75. struct keycode_list keycodes[OBS_KEY_LAST_VALUE];
  76. xcb_keycode_t min_keycode;
  77. xcb_keycode_t super_l_code;
  78. xcb_keycode_t super_r_code;
  79. /* stores a copy of the keysym map for keycodes */
  80. xcb_keysym_t *keysyms;
  81. int num_keysyms;
  82. int syms_per_code;
  83. #if USE_XINPUT
  84. bool pressed[XINPUT_MOUSE_LEN];
  85. bool update[XINPUT_MOUSE_LEN];
  86. bool button_pressed[XINPUT_MOUSE_LEN];
  87. #endif
  88. };
  89. #define MOUSE_1 (1 << 16)
  90. #define MOUSE_2 (2 << 16)
  91. #define MOUSE_3 (3 << 16)
  92. #define MOUSE_4 (4 << 16)
  93. #define MOUSE_5 (5 << 16)
  94. static int get_keysym(obs_key_t key)
  95. {
  96. switch (key) {
  97. case OBS_KEY_RETURN:
  98. return XK_Return;
  99. case OBS_KEY_ESCAPE:
  100. return XK_Escape;
  101. case OBS_KEY_TAB:
  102. return XK_Tab;
  103. case OBS_KEY_BACKSPACE:
  104. return XK_BackSpace;
  105. case OBS_KEY_INSERT:
  106. return XK_Insert;
  107. case OBS_KEY_DELETE:
  108. return XK_Delete;
  109. case OBS_KEY_PAUSE:
  110. return XK_Pause;
  111. case OBS_KEY_PRINT:
  112. return XK_Print;
  113. case OBS_KEY_HOME:
  114. return XK_Home;
  115. case OBS_KEY_END:
  116. return XK_End;
  117. case OBS_KEY_LEFT:
  118. return XK_Left;
  119. case OBS_KEY_UP:
  120. return XK_Up;
  121. case OBS_KEY_RIGHT:
  122. return XK_Right;
  123. case OBS_KEY_DOWN:
  124. return XK_Down;
  125. case OBS_KEY_PAGEUP:
  126. return XK_Prior;
  127. case OBS_KEY_PAGEDOWN:
  128. return XK_Next;
  129. case OBS_KEY_SHIFT:
  130. return XK_Shift_L;
  131. case OBS_KEY_CONTROL:
  132. return XK_Control_L;
  133. case OBS_KEY_ALT:
  134. return XK_Alt_L;
  135. case OBS_KEY_CAPSLOCK:
  136. return XK_Caps_Lock;
  137. case OBS_KEY_NUMLOCK:
  138. return XK_Num_Lock;
  139. case OBS_KEY_SCROLLLOCK:
  140. return XK_Scroll_Lock;
  141. case OBS_KEY_F1:
  142. return XK_F1;
  143. case OBS_KEY_F2:
  144. return XK_F2;
  145. case OBS_KEY_F3:
  146. return XK_F3;
  147. case OBS_KEY_F4:
  148. return XK_F4;
  149. case OBS_KEY_F5:
  150. return XK_F5;
  151. case OBS_KEY_F6:
  152. return XK_F6;
  153. case OBS_KEY_F7:
  154. return XK_F7;
  155. case OBS_KEY_F8:
  156. return XK_F8;
  157. case OBS_KEY_F9:
  158. return XK_F9;
  159. case OBS_KEY_F10:
  160. return XK_F10;
  161. case OBS_KEY_F11:
  162. return XK_F11;
  163. case OBS_KEY_F12:
  164. return XK_F12;
  165. case OBS_KEY_F13:
  166. return XK_F13;
  167. case OBS_KEY_F14:
  168. return XK_F14;
  169. case OBS_KEY_F15:
  170. return XK_F15;
  171. case OBS_KEY_F16:
  172. return XK_F16;
  173. case OBS_KEY_F17:
  174. return XK_F17;
  175. case OBS_KEY_F18:
  176. return XK_F18;
  177. case OBS_KEY_F19:
  178. return XK_F19;
  179. case OBS_KEY_F20:
  180. return XK_F20;
  181. case OBS_KEY_F21:
  182. return XK_F21;
  183. case OBS_KEY_F22:
  184. return XK_F22;
  185. case OBS_KEY_F23:
  186. return XK_F23;
  187. case OBS_KEY_F24:
  188. return XK_F24;
  189. case OBS_KEY_F25:
  190. return XK_F25;
  191. case OBS_KEY_F26:
  192. return XK_F26;
  193. case OBS_KEY_F27:
  194. return XK_F27;
  195. case OBS_KEY_F28:
  196. return XK_F28;
  197. case OBS_KEY_F29:
  198. return XK_F29;
  199. case OBS_KEY_F30:
  200. return XK_F30;
  201. case OBS_KEY_F31:
  202. return XK_F31;
  203. case OBS_KEY_F32:
  204. return XK_F32;
  205. case OBS_KEY_F33:
  206. return XK_F33;
  207. case OBS_KEY_F34:
  208. return XK_F34;
  209. case OBS_KEY_F35:
  210. return XK_F35;
  211. case OBS_KEY_MENU:
  212. return XK_Menu;
  213. case OBS_KEY_HYPER_L:
  214. return XK_Hyper_L;
  215. case OBS_KEY_HYPER_R:
  216. return XK_Hyper_R;
  217. case OBS_KEY_HELP:
  218. return XK_Help;
  219. case OBS_KEY_CANCEL:
  220. return XK_Cancel;
  221. case OBS_KEY_FIND:
  222. return XK_Find;
  223. case OBS_KEY_REDO:
  224. return XK_Redo;
  225. case OBS_KEY_UNDO:
  226. return XK_Undo;
  227. case OBS_KEY_SPACE:
  228. return XK_space;
  229. case OBS_KEY_COPY:
  230. return XF86XK_Copy;
  231. case OBS_KEY_CUT:
  232. return XF86XK_Cut;
  233. case OBS_KEY_OPEN:
  234. return XF86XK_Open;
  235. case OBS_KEY_PASTE:
  236. return XF86XK_Paste;
  237. case OBS_KEY_FRONT:
  238. return SunXK_Front;
  239. case OBS_KEY_PROPS:
  240. return SunXK_Props;
  241. case OBS_KEY_EXCLAM:
  242. return XK_exclam;
  243. case OBS_KEY_QUOTEDBL:
  244. return XK_quotedbl;
  245. case OBS_KEY_NUMBERSIGN:
  246. return XK_numbersign;
  247. case OBS_KEY_DOLLAR:
  248. return XK_dollar;
  249. case OBS_KEY_PERCENT:
  250. return XK_percent;
  251. case OBS_KEY_AMPERSAND:
  252. return XK_ampersand;
  253. case OBS_KEY_APOSTROPHE:
  254. return XK_apostrophe;
  255. case OBS_KEY_PARENLEFT:
  256. return XK_parenleft;
  257. case OBS_KEY_PARENRIGHT:
  258. return XK_parenright;
  259. case OBS_KEY_ASTERISK:
  260. return XK_asterisk;
  261. case OBS_KEY_PLUS:
  262. return XK_plus;
  263. case OBS_KEY_COMMA:
  264. return XK_comma;
  265. case OBS_KEY_MINUS:
  266. return XK_minus;
  267. case OBS_KEY_PERIOD:
  268. return XK_period;
  269. case OBS_KEY_SLASH:
  270. return XK_slash;
  271. case OBS_KEY_0:
  272. return XK_0;
  273. case OBS_KEY_1:
  274. return XK_1;
  275. case OBS_KEY_2:
  276. return XK_2;
  277. case OBS_KEY_3:
  278. return XK_3;
  279. case OBS_KEY_4:
  280. return XK_4;
  281. case OBS_KEY_5:
  282. return XK_5;
  283. case OBS_KEY_6:
  284. return XK_6;
  285. case OBS_KEY_7:
  286. return XK_7;
  287. case OBS_KEY_8:
  288. return XK_8;
  289. case OBS_KEY_9:
  290. return XK_9;
  291. case OBS_KEY_NUMEQUAL:
  292. return XK_KP_Equal;
  293. case OBS_KEY_NUMASTERISK:
  294. return XK_KP_Multiply;
  295. case OBS_KEY_NUMPLUS:
  296. return XK_KP_Add;
  297. case OBS_KEY_NUMCOMMA:
  298. return XK_KP_Separator;
  299. case OBS_KEY_NUMMINUS:
  300. return XK_KP_Subtract;
  301. case OBS_KEY_NUMPERIOD:
  302. return XK_KP_Decimal;
  303. case OBS_KEY_NUMSLASH:
  304. return XK_KP_Divide;
  305. case OBS_KEY_NUM0:
  306. return XK_KP_0;
  307. case OBS_KEY_NUM1:
  308. return XK_KP_1;
  309. case OBS_KEY_NUM2:
  310. return XK_KP_2;
  311. case OBS_KEY_NUM3:
  312. return XK_KP_3;
  313. case OBS_KEY_NUM4:
  314. return XK_KP_4;
  315. case OBS_KEY_NUM5:
  316. return XK_KP_5;
  317. case OBS_KEY_NUM6:
  318. return XK_KP_6;
  319. case OBS_KEY_NUM7:
  320. return XK_KP_7;
  321. case OBS_KEY_NUM8:
  322. return XK_KP_8;
  323. case OBS_KEY_NUM9:
  324. return XK_KP_9;
  325. case OBS_KEY_COLON:
  326. return XK_colon;
  327. case OBS_KEY_SEMICOLON:
  328. return XK_semicolon;
  329. case OBS_KEY_LESS:
  330. return XK_less;
  331. case OBS_KEY_EQUAL:
  332. return XK_equal;
  333. case OBS_KEY_GREATER:
  334. return XK_greater;
  335. case OBS_KEY_QUESTION:
  336. return XK_question;
  337. case OBS_KEY_AT:
  338. return XK_at;
  339. case OBS_KEY_A:
  340. return XK_A;
  341. case OBS_KEY_B:
  342. return XK_B;
  343. case OBS_KEY_C:
  344. return XK_C;
  345. case OBS_KEY_D:
  346. return XK_D;
  347. case OBS_KEY_E:
  348. return XK_E;
  349. case OBS_KEY_F:
  350. return XK_F;
  351. case OBS_KEY_G:
  352. return XK_G;
  353. case OBS_KEY_H:
  354. return XK_H;
  355. case OBS_KEY_I:
  356. return XK_I;
  357. case OBS_KEY_J:
  358. return XK_J;
  359. case OBS_KEY_K:
  360. return XK_K;
  361. case OBS_KEY_L:
  362. return XK_L;
  363. case OBS_KEY_M:
  364. return XK_M;
  365. case OBS_KEY_N:
  366. return XK_N;
  367. case OBS_KEY_O:
  368. return XK_O;
  369. case OBS_KEY_P:
  370. return XK_P;
  371. case OBS_KEY_Q:
  372. return XK_Q;
  373. case OBS_KEY_R:
  374. return XK_R;
  375. case OBS_KEY_S:
  376. return XK_S;
  377. case OBS_KEY_T:
  378. return XK_T;
  379. case OBS_KEY_U:
  380. return XK_U;
  381. case OBS_KEY_V:
  382. return XK_V;
  383. case OBS_KEY_W:
  384. return XK_W;
  385. case OBS_KEY_X:
  386. return XK_X;
  387. case OBS_KEY_Y:
  388. return XK_Y;
  389. case OBS_KEY_Z:
  390. return XK_Z;
  391. case OBS_KEY_BRACKETLEFT:
  392. return XK_bracketleft;
  393. case OBS_KEY_BACKSLASH:
  394. return XK_backslash;
  395. case OBS_KEY_BRACKETRIGHT:
  396. return XK_bracketright;
  397. case OBS_KEY_ASCIICIRCUM:
  398. return XK_asciicircum;
  399. case OBS_KEY_UNDERSCORE:
  400. return XK_underscore;
  401. case OBS_KEY_QUOTELEFT:
  402. return XK_quoteleft;
  403. case OBS_KEY_BRACELEFT:
  404. return XK_braceleft;
  405. case OBS_KEY_BAR:
  406. return XK_bar;
  407. case OBS_KEY_BRACERIGHT:
  408. return XK_braceright;
  409. case OBS_KEY_ASCIITILDE:
  410. return XK_grave;
  411. case OBS_KEY_NOBREAKSPACE:
  412. return XK_nobreakspace;
  413. case OBS_KEY_EXCLAMDOWN:
  414. return XK_exclamdown;
  415. case OBS_KEY_CENT:
  416. return XK_cent;
  417. case OBS_KEY_STERLING:
  418. return XK_sterling;
  419. case OBS_KEY_CURRENCY:
  420. return XK_currency;
  421. case OBS_KEY_YEN:
  422. return XK_yen;
  423. case OBS_KEY_BROKENBAR:
  424. return XK_brokenbar;
  425. case OBS_KEY_SECTION:
  426. return XK_section;
  427. case OBS_KEY_DIAERESIS:
  428. return XK_diaeresis;
  429. case OBS_KEY_COPYRIGHT:
  430. return XK_copyright;
  431. case OBS_KEY_ORDFEMININE:
  432. return XK_ordfeminine;
  433. case OBS_KEY_GUILLEMOTLEFT:
  434. return XK_guillemotleft;
  435. case OBS_KEY_NOTSIGN:
  436. return XK_notsign;
  437. case OBS_KEY_HYPHEN:
  438. return XK_hyphen;
  439. case OBS_KEY_REGISTERED:
  440. return XK_registered;
  441. case OBS_KEY_MACRON:
  442. return XK_macron;
  443. case OBS_KEY_DEGREE:
  444. return XK_degree;
  445. case OBS_KEY_PLUSMINUS:
  446. return XK_plusminus;
  447. case OBS_KEY_TWOSUPERIOR:
  448. return XK_twosuperior;
  449. case OBS_KEY_THREESUPERIOR:
  450. return XK_threesuperior;
  451. case OBS_KEY_ACUTE:
  452. return XK_acute;
  453. case OBS_KEY_MU:
  454. return XK_mu;
  455. case OBS_KEY_PARAGRAPH:
  456. return XK_paragraph;
  457. case OBS_KEY_PERIODCENTERED:
  458. return XK_periodcentered;
  459. case OBS_KEY_CEDILLA:
  460. return XK_cedilla;
  461. case OBS_KEY_ONESUPERIOR:
  462. return XK_onesuperior;
  463. case OBS_KEY_MASCULINE:
  464. return XK_masculine;
  465. case OBS_KEY_GUILLEMOTRIGHT:
  466. return XK_guillemotright;
  467. case OBS_KEY_ONEQUARTER:
  468. return XK_onequarter;
  469. case OBS_KEY_ONEHALF:
  470. return XK_onehalf;
  471. case OBS_KEY_THREEQUARTERS:
  472. return XK_threequarters;
  473. case OBS_KEY_QUESTIONDOWN:
  474. return XK_questiondown;
  475. case OBS_KEY_AGRAVE:
  476. return XK_Agrave;
  477. case OBS_KEY_AACUTE:
  478. return XK_Aacute;
  479. case OBS_KEY_ACIRCUMFLEX:
  480. return XK_Acircumflex;
  481. case OBS_KEY_ATILDE:
  482. return XK_Atilde;
  483. case OBS_KEY_ADIAERESIS:
  484. return XK_Adiaeresis;
  485. case OBS_KEY_ARING:
  486. return XK_Aring;
  487. case OBS_KEY_AE:
  488. return XK_AE;
  489. case OBS_KEY_CCEDILLA:
  490. return XK_cedilla;
  491. case OBS_KEY_EGRAVE:
  492. return XK_Egrave;
  493. case OBS_KEY_EACUTE:
  494. return XK_Eacute;
  495. case OBS_KEY_ECIRCUMFLEX:
  496. return XK_Ecircumflex;
  497. case OBS_KEY_EDIAERESIS:
  498. return XK_Ediaeresis;
  499. case OBS_KEY_IGRAVE:
  500. return XK_Igrave;
  501. case OBS_KEY_IACUTE:
  502. return XK_Iacute;
  503. case OBS_KEY_ICIRCUMFLEX:
  504. return XK_Icircumflex;
  505. case OBS_KEY_IDIAERESIS:
  506. return XK_Idiaeresis;
  507. case OBS_KEY_ETH:
  508. return XK_ETH;
  509. case OBS_KEY_NTILDE:
  510. return XK_Ntilde;
  511. case OBS_KEY_OGRAVE:
  512. return XK_Ograve;
  513. case OBS_KEY_OACUTE:
  514. return XK_Oacute;
  515. case OBS_KEY_OCIRCUMFLEX:
  516. return XK_Ocircumflex;
  517. case OBS_KEY_ODIAERESIS:
  518. return XK_Odiaeresis;
  519. case OBS_KEY_MULTIPLY:
  520. return XK_multiply;
  521. case OBS_KEY_OOBLIQUE:
  522. return XK_Ooblique;
  523. case OBS_KEY_UGRAVE:
  524. return XK_Ugrave;
  525. case OBS_KEY_UACUTE:
  526. return XK_Uacute;
  527. case OBS_KEY_UCIRCUMFLEX:
  528. return XK_Ucircumflex;
  529. case OBS_KEY_UDIAERESIS:
  530. return XK_Udiaeresis;
  531. case OBS_KEY_YACUTE:
  532. return XK_Yacute;
  533. case OBS_KEY_THORN:
  534. return XK_Thorn;
  535. case OBS_KEY_SSHARP:
  536. return XK_ssharp;
  537. case OBS_KEY_DIVISION:
  538. return XK_division;
  539. case OBS_KEY_YDIAERESIS:
  540. return XK_Ydiaeresis;
  541. case OBS_KEY_MULTI_KEY:
  542. return XK_Multi_key;
  543. case OBS_KEY_CODEINPUT:
  544. return XK_Codeinput;
  545. case OBS_KEY_SINGLECANDIDATE:
  546. return XK_SingleCandidate;
  547. case OBS_KEY_MULTIPLECANDIDATE:
  548. return XK_MultipleCandidate;
  549. case OBS_KEY_PREVIOUSCANDIDATE:
  550. return XK_PreviousCandidate;
  551. case OBS_KEY_MODE_SWITCH:
  552. return XK_Mode_switch;
  553. case OBS_KEY_KANJI:
  554. return XK_Kanji;
  555. case OBS_KEY_MUHENKAN:
  556. return XK_Muhenkan;
  557. case OBS_KEY_HENKAN:
  558. return XK_Henkan;
  559. case OBS_KEY_ROMAJI:
  560. return XK_Romaji;
  561. case OBS_KEY_HIRAGANA:
  562. return XK_Hiragana;
  563. case OBS_KEY_KATAKANA:
  564. return XK_Katakana;
  565. case OBS_KEY_HIRAGANA_KATAKANA:
  566. return XK_Hiragana_Katakana;
  567. case OBS_KEY_ZENKAKU:
  568. return XK_Zenkaku;
  569. case OBS_KEY_HANKAKU:
  570. return XK_Hankaku;
  571. case OBS_KEY_ZENKAKU_HANKAKU:
  572. return XK_Zenkaku_Hankaku;
  573. case OBS_KEY_TOUROKU:
  574. return XK_Touroku;
  575. case OBS_KEY_MASSYO:
  576. return XK_Massyo;
  577. case OBS_KEY_KANA_LOCK:
  578. return XK_Kana_Lock;
  579. case OBS_KEY_KANA_SHIFT:
  580. return XK_Kana_Shift;
  581. case OBS_KEY_EISU_SHIFT:
  582. return XK_Eisu_Shift;
  583. case OBS_KEY_EISU_TOGGLE:
  584. return XK_Eisu_toggle;
  585. case OBS_KEY_HANGUL:
  586. return XK_Hangul;
  587. case OBS_KEY_HANGUL_START:
  588. return XK_Hangul_Start;
  589. case OBS_KEY_HANGUL_END:
  590. return XK_Hangul_End;
  591. case OBS_KEY_HANGUL_HANJA:
  592. return XK_Hangul_Hanja;
  593. case OBS_KEY_HANGUL_JAMO:
  594. return XK_Hangul_Jamo;
  595. case OBS_KEY_HANGUL_ROMAJA:
  596. return XK_Hangul_Romaja;
  597. case OBS_KEY_HANGUL_BANJA:
  598. return XK_Hangul_Banja;
  599. case OBS_KEY_HANGUL_PREHANJA:
  600. return XK_Hangul_PreHanja;
  601. case OBS_KEY_HANGUL_POSTHANJA:
  602. return XK_Hangul_PostHanja;
  603. case OBS_KEY_HANGUL_SPECIAL:
  604. return XK_Hangul_Special;
  605. case OBS_KEY_DEAD_GRAVE:
  606. return XK_dead_grave;
  607. case OBS_KEY_DEAD_ACUTE:
  608. return XK_dead_acute;
  609. case OBS_KEY_DEAD_CIRCUMFLEX:
  610. return XK_dead_circumflex;
  611. case OBS_KEY_DEAD_TILDE:
  612. return XK_dead_tilde;
  613. case OBS_KEY_DEAD_MACRON:
  614. return XK_dead_macron;
  615. case OBS_KEY_DEAD_BREVE:
  616. return XK_dead_breve;
  617. case OBS_KEY_DEAD_ABOVEDOT:
  618. return XK_dead_abovedot;
  619. case OBS_KEY_DEAD_DIAERESIS:
  620. return XK_dead_diaeresis;
  621. case OBS_KEY_DEAD_ABOVERING:
  622. return XK_dead_abovering;
  623. case OBS_KEY_DEAD_DOUBLEACUTE:
  624. return XK_dead_doubleacute;
  625. case OBS_KEY_DEAD_CARON:
  626. return XK_dead_caron;
  627. case OBS_KEY_DEAD_CEDILLA:
  628. return XK_dead_cedilla;
  629. case OBS_KEY_DEAD_OGONEK:
  630. return XK_dead_ogonek;
  631. case OBS_KEY_DEAD_IOTA:
  632. return XK_dead_iota;
  633. case OBS_KEY_DEAD_VOICED_SOUND:
  634. return XK_dead_voiced_sound;
  635. case OBS_KEY_DEAD_SEMIVOICED_SOUND:
  636. return XK_dead_semivoiced_sound;
  637. case OBS_KEY_DEAD_BELOWDOT:
  638. return XK_dead_belowdot;
  639. case OBS_KEY_DEAD_HOOK:
  640. return XK_dead_hook;
  641. case OBS_KEY_DEAD_HORN:
  642. return XK_dead_horn;
  643. case OBS_KEY_MOUSE1:
  644. return MOUSE_1;
  645. case OBS_KEY_MOUSE2:
  646. return MOUSE_2;
  647. case OBS_KEY_MOUSE3:
  648. return MOUSE_3;
  649. case OBS_KEY_MOUSE4:
  650. return MOUSE_4;
  651. case OBS_KEY_MOUSE5:
  652. return MOUSE_5;
  653. /* TODO: Implement keys for non-US keyboards */
  654. default:;
  655. }
  656. return 0;
  657. }
  658. static inline void fill_base_keysyms(struct obs_core_hotkeys *hotkeys)
  659. {
  660. for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)
  661. hotkeys->platform_context->base_keysyms[i] = get_keysym(i);
  662. }
  663. static obs_key_t key_from_base_keysym(obs_hotkeys_platform_t *context,
  664. xcb_keysym_t code)
  665. {
  666. for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
  667. if (context->base_keysyms[i] == (xcb_keysym_t)code) {
  668. return (obs_key_t)i;
  669. }
  670. }
  671. return OBS_KEY_NONE;
  672. }
  673. static inline void add_key(obs_hotkeys_platform_t *context, obs_key_t key,
  674. int code)
  675. {
  676. xcb_keycode_t kc = (xcb_keycode_t)code;
  677. da_push_back(context->keycodes[key].list, &kc);
  678. if (context->keycodes[key].list.num > 1) {
  679. blog(LOG_DEBUG,
  680. "found alternate keycode %d for %s "
  681. "which already has keycode %d",
  682. code, obs_key_to_name(key),
  683. (int)context->keycodes[key].list.array[0]);
  684. }
  685. }
  686. static inline bool fill_keycodes(struct obs_core_hotkeys *hotkeys)
  687. {
  688. obs_hotkeys_platform_t *context = hotkeys->platform_context;
  689. xcb_connection_t *connection = XGetXCBConnection(context->display);
  690. const struct xcb_setup_t *setup = xcb_get_setup(connection);
  691. xcb_get_keyboard_mapping_cookie_t cookie;
  692. xcb_get_keyboard_mapping_reply_t *reply;
  693. xcb_generic_error_t *error = NULL;
  694. int code;
  695. int mincode = setup->min_keycode;
  696. int maxcode = setup->max_keycode;
  697. context->min_keycode = setup->min_keycode;
  698. cookie = xcb_get_keyboard_mapping(connection, mincode,
  699. maxcode - mincode + 1);
  700. reply = xcb_get_keyboard_mapping_reply(connection, cookie, &error);
  701. if (error || !reply) {
  702. blog(LOG_WARNING, "xcb_get_keyboard_mapping_reply failed");
  703. goto error1;
  704. }
  705. const xcb_keysym_t *keysyms = xcb_get_keyboard_mapping_keysyms(reply);
  706. int syms_per_code = (int)reply->keysyms_per_keycode;
  707. context->num_keysyms = (maxcode - mincode + 1) * syms_per_code;
  708. context->syms_per_code = syms_per_code;
  709. context->keysyms =
  710. bmemdup(keysyms, sizeof(xcb_keysym_t) * context->num_keysyms);
  711. for (code = mincode; code <= maxcode; code++) {
  712. const xcb_keysym_t *sym;
  713. obs_key_t key;
  714. sym = &keysyms[(code - mincode) * syms_per_code];
  715. for (int i = 0; i < syms_per_code; i++) {
  716. if (!sym[i])
  717. break;
  718. if (sym[i] == XK_Super_L) {
  719. context->super_l_code = code;
  720. break;
  721. } else if (sym[i] == XK_Super_R) {
  722. context->super_r_code = code;
  723. break;
  724. } else {
  725. key = key_from_base_keysym(context, sym[i]);
  726. if (key != OBS_KEY_NONE) {
  727. add_key(context, key, code);
  728. break;
  729. }
  730. }
  731. }
  732. }
  733. error1:
  734. free(reply);
  735. free(error);
  736. return error != NULL || reply == NULL;
  737. }
  738. static xcb_screen_t *default_screen(obs_hotkeys_platform_t *context,
  739. xcb_connection_t *connection)
  740. {
  741. int def_screen_idx = XDefaultScreen(context->display);
  742. xcb_screen_iterator_t iter;
  743. iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
  744. while (iter.rem) {
  745. if (def_screen_idx-- == 0)
  746. return iter.data;
  747. xcb_screen_next(&iter);
  748. }
  749. return NULL;
  750. }
  751. static inline xcb_window_t root_window(obs_hotkeys_platform_t *context,
  752. xcb_connection_t *connection)
  753. {
  754. xcb_screen_t *screen = default_screen(context, connection);
  755. if (screen)
  756. return screen->root;
  757. return 0;
  758. }
  759. #if USE_XINPUT
  760. static inline void registerMouseEvents(struct obs_core_hotkeys *hotkeys)
  761. {
  762. obs_hotkeys_platform_t *context = hotkeys->platform_context;
  763. xcb_connection_t *connection = XGetXCBConnection(context->display);
  764. xcb_window_t window = root_window(context, connection);
  765. struct {
  766. xcb_input_event_mask_t head;
  767. xcb_input_xi_event_mask_t mask;
  768. } mask;
  769. mask.head.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
  770. mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t);
  771. mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS |
  772. XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE;
  773. xcb_input_xi_select_events(connection, window, 1, &mask.head);
  774. xcb_flush(connection);
  775. }
  776. #endif
  777. static bool obs_nix_x11_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
  778. {
  779. Display *display = XOpenDisplay(NULL);
  780. if (!display)
  781. return false;
  782. hotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t));
  783. hotkeys->platform_context->display = display;
  784. #if USE_XINPUT
  785. registerMouseEvents(hotkeys);
  786. #endif
  787. fill_base_keysyms(hotkeys);
  788. fill_keycodes(hotkeys);
  789. return true;
  790. }
  791. static void obs_nix_x11_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
  792. {
  793. obs_hotkeys_platform_t *context = hotkeys->platform_context;
  794. for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)
  795. da_free(context->keycodes[i].list);
  796. bfree(context->keysyms);
  797. bfree(context);
  798. hotkeys->platform_context = NULL;
  799. }
  800. static bool mouse_button_pressed(xcb_connection_t *connection,
  801. obs_hotkeys_platform_t *context, obs_key_t key)
  802. {
  803. bool ret = false;
  804. #if USE_XINPUT
  805. memset(context->pressed, 0, XINPUT_MOUSE_LEN);
  806. memset(context->update, 0, XINPUT_MOUSE_LEN);
  807. xcb_generic_event_t *ev;
  808. while ((ev = xcb_poll_for_event(connection))) {
  809. if ((ev->response_type & ~80) == XCB_GE_GENERIC) {
  810. switch (((xcb_ge_event_t *)ev)->event_type) {
  811. case XCB_INPUT_RAW_BUTTON_PRESS: {
  812. xcb_input_raw_button_press_event_t *mot;
  813. mot = (xcb_input_raw_button_press_event_t *)ev;
  814. if (mot->detail < XINPUT_MOUSE_LEN) {
  815. context->pressed[mot->detail - 1] =
  816. true;
  817. context->update[mot->detail - 1] = true;
  818. } else {
  819. blog(LOG_WARNING, "Unsupported button");
  820. }
  821. break;
  822. }
  823. case XCB_INPUT_RAW_BUTTON_RELEASE: {
  824. xcb_input_raw_button_release_event_t *mot;
  825. mot = (xcb_input_raw_button_release_event_t *)ev;
  826. if (mot->detail < XINPUT_MOUSE_LEN)
  827. context->update[mot->detail - 1] = true;
  828. else
  829. blog(LOG_WARNING, "Unsupported button");
  830. break;
  831. }
  832. default:
  833. break;
  834. }
  835. }
  836. free(ev);
  837. }
  838. // Mouse 2 for OBS is Right Click and Mouse 3 is Wheel Click.
  839. // Mouse Wheel axis clicks (xinput mot->detail 4 5 6 7) are ignored.
  840. switch (key) {
  841. case OBS_KEY_MOUSE1:
  842. ret = context->pressed[0] || context->button_pressed[0];
  843. break;
  844. case OBS_KEY_MOUSE2:
  845. ret = context->pressed[2] || context->button_pressed[2];
  846. break;
  847. case OBS_KEY_MOUSE3:
  848. ret = context->pressed[1] || context->button_pressed[1];
  849. break;
  850. case OBS_KEY_MOUSE4:
  851. ret = context->pressed[7] || context->button_pressed[7];
  852. break;
  853. case OBS_KEY_MOUSE5:
  854. ret = context->pressed[8] || context->button_pressed[8];
  855. break;
  856. case OBS_KEY_MOUSE6:
  857. ret = context->pressed[9] || context->button_pressed[9];
  858. break;
  859. case OBS_KEY_MOUSE7:
  860. ret = context->pressed[10] || context->button_pressed[10];
  861. break;
  862. case OBS_KEY_MOUSE8:
  863. ret = context->pressed[11] || context->button_pressed[11];
  864. break;
  865. case OBS_KEY_MOUSE9:
  866. ret = context->pressed[12] || context->button_pressed[12];
  867. break;
  868. case OBS_KEY_MOUSE10:
  869. ret = context->pressed[13] || context->button_pressed[13];
  870. break;
  871. case OBS_KEY_MOUSE11:
  872. ret = context->pressed[14] || context->button_pressed[14];
  873. break;
  874. case OBS_KEY_MOUSE12:
  875. ret = context->pressed[15] || context->button_pressed[15];
  876. break;
  877. case OBS_KEY_MOUSE13:
  878. ret = context->pressed[16] || context->button_pressed[16];
  879. break;
  880. case OBS_KEY_MOUSE14:
  881. ret = context->pressed[17] || context->button_pressed[17];
  882. break;
  883. case OBS_KEY_MOUSE15:
  884. ret = context->pressed[18] || context->button_pressed[18];
  885. break;
  886. case OBS_KEY_MOUSE16:
  887. ret = context->pressed[19] || context->button_pressed[19];
  888. break;
  889. case OBS_KEY_MOUSE17:
  890. ret = context->pressed[20] || context->button_pressed[20];
  891. break;
  892. case OBS_KEY_MOUSE18:
  893. ret = context->pressed[21] || context->button_pressed[21];
  894. break;
  895. case OBS_KEY_MOUSE19:
  896. ret = context->pressed[22] || context->button_pressed[22];
  897. break;
  898. case OBS_KEY_MOUSE20:
  899. ret = context->pressed[23] || context->button_pressed[23];
  900. break;
  901. case OBS_KEY_MOUSE21:
  902. ret = context->pressed[24] || context->button_pressed[24];
  903. break;
  904. case OBS_KEY_MOUSE22:
  905. ret = context->pressed[25] || context->button_pressed[25];
  906. break;
  907. case OBS_KEY_MOUSE23:
  908. ret = context->pressed[26] || context->button_pressed[26];
  909. break;
  910. case OBS_KEY_MOUSE24:
  911. ret = context->pressed[27] || context->button_pressed[27];
  912. break;
  913. case OBS_KEY_MOUSE25:
  914. ret = context->pressed[28] || context->button_pressed[28];
  915. break;
  916. case OBS_KEY_MOUSE26:
  917. ret = context->pressed[29] || context->button_pressed[29];
  918. break;
  919. case OBS_KEY_MOUSE27:
  920. ret = context->pressed[30] || context->button_pressed[30];
  921. break;
  922. case OBS_KEY_MOUSE28:
  923. ret = context->pressed[31] || context->button_pressed[31];
  924. break;
  925. case OBS_KEY_MOUSE29:
  926. ret = context->pressed[32] || context->button_pressed[32];
  927. break;
  928. default:
  929. break;
  930. }
  931. for (int i = 0; i != XINPUT_MOUSE_LEN; i++)
  932. if (context->update[i])
  933. context->button_pressed[i] = context->pressed[i];
  934. #else
  935. xcb_generic_error_t *error = NULL;
  936. xcb_query_pointer_cookie_t qpc;
  937. xcb_query_pointer_reply_t *reply;
  938. qpc = xcb_query_pointer(connection, root_window(context, connection));
  939. reply = xcb_query_pointer_reply(connection, qpc, &error);
  940. if (error) {
  941. blog(LOG_WARNING, "xcb_query_pointer_reply failed");
  942. } else {
  943. uint16_t buttons = reply->mask;
  944. switch (key) {
  945. case OBS_KEY_MOUSE1:
  946. ret = buttons & XCB_BUTTON_MASK_1;
  947. break;
  948. case OBS_KEY_MOUSE2:
  949. ret = buttons & XCB_BUTTON_MASK_3;
  950. break;
  951. case OBS_KEY_MOUSE3:
  952. ret = buttons & XCB_BUTTON_MASK_2;
  953. break;
  954. default:;
  955. }
  956. }
  957. free(reply);
  958. free(error);
  959. #endif
  960. return ret;
  961. }
  962. static inline bool keycode_pressed(xcb_query_keymap_reply_t *reply,
  963. xcb_keycode_t code)
  964. {
  965. return (reply->keys[code / 8] & (1 << (code % 8))) != 0;
  966. }
  967. static bool key_pressed(xcb_connection_t *connection,
  968. obs_hotkeys_platform_t *context, obs_key_t key)
  969. {
  970. struct keycode_list *codes = &context->keycodes[key];
  971. xcb_generic_error_t *error = NULL;
  972. xcb_query_keymap_reply_t *reply;
  973. bool pressed = false;
  974. reply = xcb_query_keymap_reply(connection, xcb_query_keymap(connection),
  975. &error);
  976. if (error) {
  977. blog(LOG_WARNING, "xcb_query_keymap failed");
  978. } else if (key == OBS_KEY_META) {
  979. pressed = keycode_pressed(reply, context->super_l_code) ||
  980. keycode_pressed(reply, context->super_r_code);
  981. } else {
  982. for (size_t i = 0; i < codes->list.num; i++) {
  983. if (keycode_pressed(reply, codes->list.array[i])) {
  984. pressed = true;
  985. break;
  986. }
  987. }
  988. }
  989. free(reply);
  990. free(error);
  991. return pressed;
  992. }
  993. static bool
  994. obs_nix_x11_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context,
  995. obs_key_t key)
  996. {
  997. xcb_connection_t *conn = XGetXCBConnection(context->display);
  998. if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {
  999. return mouse_button_pressed(conn, context, key);
  1000. } else {
  1001. return key_pressed(conn, context, key);
  1002. }
  1003. }
  1004. static bool get_key_translation(struct dstr *dstr, xcb_keycode_t keycode)
  1005. {
  1006. xcb_connection_t *connection;
  1007. char name[128];
  1008. connection = XGetXCBConnection(obs->hotkeys.platform_context->display);
  1009. XKeyEvent event = {0};
  1010. event.type = KeyPress;
  1011. event.display = obs->hotkeys.platform_context->display;
  1012. event.keycode = keycode;
  1013. event.root = root_window(obs->hotkeys.platform_context, connection);
  1014. event.window = event.root;
  1015. if (keycode) {
  1016. int len = XLookupString(&event, name, 128, NULL, NULL);
  1017. if (len) {
  1018. dstr_ncopy(dstr, name, len);
  1019. dstr_to_upper(dstr);
  1020. return true;
  1021. }
  1022. }
  1023. return false;
  1024. }
  1025. static void obs_nix_x11_key_to_str(obs_key_t key, struct dstr *dstr)
  1026. {
  1027. if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {
  1028. if (obs->hotkeys.translations[key]) {
  1029. dstr_copy(dstr, obs->hotkeys.translations[key]);
  1030. } else {
  1031. dstr_printf(dstr, "Mouse %d",
  1032. (int)(key - OBS_KEY_MOUSE1 + 1));
  1033. }
  1034. return;
  1035. }
  1036. if (key >= OBS_KEY_NUM0 && key <= OBS_KEY_NUM9) {
  1037. if (obs->hotkeys.translations[key]) {
  1038. dstr_copy(dstr, obs->hotkeys.translations[key]);
  1039. } else {
  1040. dstr_printf(dstr, "Numpad %d",
  1041. (int)(key - OBS_KEY_NUM0));
  1042. }
  1043. return;
  1044. }
  1045. #define translate_key(key, def) \
  1046. dstr_copy(dstr, obs_get_hotkey_translation(key, def))
  1047. switch (key) {
  1048. case OBS_KEY_INSERT:
  1049. return translate_key(key, "Insert");
  1050. case OBS_KEY_DELETE:
  1051. return translate_key(key, "Delete");
  1052. case OBS_KEY_HOME:
  1053. return translate_key(key, "Home");
  1054. case OBS_KEY_END:
  1055. return translate_key(key, "End");
  1056. case OBS_KEY_PAGEUP:
  1057. return translate_key(key, "Page Up");
  1058. case OBS_KEY_PAGEDOWN:
  1059. return translate_key(key, "Page Down");
  1060. case OBS_KEY_NUMLOCK:
  1061. return translate_key(key, "Num Lock");
  1062. case OBS_KEY_SCROLLLOCK:
  1063. return translate_key(key, "Scroll Lock");
  1064. case OBS_KEY_CAPSLOCK:
  1065. return translate_key(key, "Caps Lock");
  1066. case OBS_KEY_BACKSPACE:
  1067. return translate_key(key, "Backspace");
  1068. case OBS_KEY_TAB:
  1069. return translate_key(key, "Tab");
  1070. case OBS_KEY_PRINT:
  1071. return translate_key(key, "Print");
  1072. case OBS_KEY_PAUSE:
  1073. return translate_key(key, "Pause");
  1074. case OBS_KEY_LEFT:
  1075. return translate_key(key, "Left");
  1076. case OBS_KEY_RIGHT:
  1077. return translate_key(key, "Right");
  1078. case OBS_KEY_UP:
  1079. return translate_key(key, "Up");
  1080. case OBS_KEY_DOWN:
  1081. return translate_key(key, "Down");
  1082. case OBS_KEY_SHIFT:
  1083. return translate_key(key, "Shift");
  1084. case OBS_KEY_ALT:
  1085. return translate_key(key, "Alt");
  1086. case OBS_KEY_CONTROL:
  1087. return translate_key(key, "Control");
  1088. case OBS_KEY_META:
  1089. return translate_key(key, "Super");
  1090. case OBS_KEY_MENU:
  1091. return translate_key(key, "Menu");
  1092. case OBS_KEY_NUMASTERISK:
  1093. return translate_key(key, "Numpad *");
  1094. case OBS_KEY_NUMPLUS:
  1095. return translate_key(key, "Numpad +");
  1096. case OBS_KEY_NUMCOMMA:
  1097. return translate_key(key, "Numpad ,");
  1098. case OBS_KEY_NUMPERIOD:
  1099. return translate_key(key, "Numpad .");
  1100. case OBS_KEY_NUMSLASH:
  1101. return translate_key(key, "Numpad /");
  1102. case OBS_KEY_SPACE:
  1103. return translate_key(key, "Space");
  1104. case OBS_KEY_ESCAPE:
  1105. return translate_key(key, "Escape");
  1106. default:;
  1107. }
  1108. if (key >= OBS_KEY_F1 && key <= OBS_KEY_F35) {
  1109. dstr_printf(dstr, "F%d", (int)(key - OBS_KEY_F1 + 1));
  1110. return;
  1111. }
  1112. obs_hotkeys_platform_t *context = obs->hotkeys.platform_context;
  1113. struct keycode_list *keycodes = &context->keycodes[key];
  1114. for (size_t i = 0; i < keycodes->list.num; i++) {
  1115. if (get_key_translation(dstr, keycodes->list.array[i])) {
  1116. break;
  1117. }
  1118. }
  1119. if (key != OBS_KEY_NONE && dstr_is_empty(dstr)) {
  1120. dstr_copy(dstr, obs_key_to_name(key));
  1121. }
  1122. }
  1123. static obs_key_t key_from_keycode(obs_hotkeys_platform_t *context,
  1124. xcb_keycode_t code)
  1125. {
  1126. for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
  1127. struct keycode_list *codes = &context->keycodes[i];
  1128. for (size_t j = 0; j < codes->list.num; j++) {
  1129. if (codes->list.array[j] == code) {
  1130. return (obs_key_t)i;
  1131. }
  1132. }
  1133. }
  1134. return OBS_KEY_NONE;
  1135. }
  1136. static obs_key_t obs_nix_x11_key_from_virtual_key(int sym)
  1137. {
  1138. obs_hotkeys_platform_t *context = obs->hotkeys.platform_context;
  1139. const xcb_keysym_t *keysyms = context->keysyms;
  1140. int syms_per_code = context->syms_per_code;
  1141. int num_keysyms = context->num_keysyms;
  1142. if (sym == 0)
  1143. return OBS_KEY_NONE;
  1144. for (int i = 0; i < num_keysyms; i++) {
  1145. if (keysyms[i] == (xcb_keysym_t)sym) {
  1146. xcb_keycode_t code = (xcb_keycode_t)(i / syms_per_code);
  1147. code += context->min_keycode;
  1148. obs_key_t key = key_from_keycode(context, code);
  1149. return key;
  1150. }
  1151. }
  1152. return OBS_KEY_NONE;
  1153. }
  1154. static int obs_nix_x11_key_to_virtual_key(obs_key_t key)
  1155. {
  1156. if (key == OBS_KEY_META)
  1157. return XK_Super_L;
  1158. return (int)obs->hotkeys.platform_context->base_keysyms[(int)key];
  1159. }
  1160. static const struct obs_nix_hotkeys_vtable x11_hotkeys_vtable = {
  1161. .init = obs_nix_x11_hotkeys_platform_init,
  1162. .free = obs_nix_x11_hotkeys_platform_free,
  1163. .is_pressed = obs_nix_x11_hotkeys_platform_is_pressed,
  1164. .key_to_str = obs_nix_x11_key_to_str,
  1165. .key_from_virtual_key = obs_nix_x11_key_from_virtual_key,
  1166. .key_to_virtual_key = obs_nix_x11_key_to_virtual_key,
  1167. };
  1168. const struct obs_nix_hotkeys_vtable *obs_nix_x11_get_hotkeys_vtable(void)
  1169. {
  1170. return &x11_hotkeys_vtable;
  1171. }