1
0

obs-nix.c 36 KB

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