1
0

obs-cocoa.c 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327
  1. /******************************************************************************
  2. Copyright (C) 2013 by Ruwen Hahn <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "util/platform.h"
  15. #include "util/dstr.h"
  16. #include "obs.h"
  17. #include "obs-internal.h"
  18. #include <unistd.h>
  19. #include <sys/types.h>
  20. #include <sys/sysctl.h>
  21. #include <objc/objc.h>
  22. #include <Carbon/Carbon.h>
  23. #include <IOKit/hid/IOHIDDevice.h>
  24. #include <IOKit/hid/IOHIDManager.h>
  25. const char *get_module_extension(void)
  26. {
  27. return ".so";
  28. }
  29. static const char *module_bin[] = {
  30. "../obs-plugins",
  31. OBS_INSTALL_PREFIX "obs-plugins",
  32. };
  33. static const char *module_data[] = {
  34. "../data/obs-plugins/%module%",
  35. OBS_INSTALL_DATA_PATH "obs-plugins/%module%",
  36. };
  37. static const int module_patterns_size =
  38. sizeof(module_bin)/sizeof(module_bin[0]);
  39. void add_default_module_paths(void)
  40. {
  41. for (int i = 0; i < module_patterns_size; i++)
  42. obs_add_module_path(module_bin[i], module_data[i]);
  43. }
  44. char *find_libobs_data_file(const char *file)
  45. {
  46. struct dstr path;
  47. dstr_init_copy(&path, OBS_INSTALL_DATA_PATH "/libobs/");
  48. dstr_cat(&path, file);
  49. return path.array;
  50. }
  51. static void log_processor_name(void)
  52. {
  53. char *name = NULL;
  54. size_t size;
  55. int ret;
  56. ret = sysctlbyname("machdep.cpu.brand_string", NULL, &size, NULL, 0);
  57. if (ret != 0)
  58. return;
  59. name = malloc(size);
  60. ret = sysctlbyname("machdep.cpu.brand_string", name, &size, NULL, 0);
  61. if (ret == 0)
  62. blog(LOG_INFO, "CPU Name: %s", name);
  63. free(name);
  64. }
  65. static void log_processor_speed(void)
  66. {
  67. size_t size;
  68. long long freq;
  69. int ret;
  70. size = sizeof(freq);
  71. ret = sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0);
  72. if (ret == 0)
  73. blog(LOG_INFO, "CPU Speed: %lldMHz", freq / 1000000);
  74. }
  75. static void log_processor_cores(void)
  76. {
  77. blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d",
  78. os_get_physical_cores(), os_get_logical_cores());
  79. }
  80. static void log_available_memory(void)
  81. {
  82. size_t size;
  83. long long memory_available;
  84. int ret;
  85. size = sizeof(memory_available);
  86. ret = sysctlbyname("hw.memsize", &memory_available, &size, NULL, 0);
  87. if (ret == 0)
  88. blog(LOG_INFO, "Physical Memory: %lldMB Total",
  89. memory_available / 1024 / 1024);
  90. }
  91. static void log_os_name(id pi, SEL UTF8String)
  92. {
  93. unsigned long os_id = (unsigned long)objc_msgSend(pi,
  94. sel_registerName("operatingSystem"));
  95. id os = objc_msgSend(pi,
  96. sel_registerName("operatingSystemName"));
  97. const char *name = (const char*)objc_msgSend(os, UTF8String);
  98. if (os_id == 5 /*NSMACHOperatingSystem*/) {
  99. blog(LOG_INFO, "OS Name: Mac OS X (%s)", name);
  100. return;
  101. }
  102. blog(LOG_INFO, "OS Name: %s", name ? name : "Unknown");
  103. }
  104. static void log_os_version(id pi, SEL UTF8String)
  105. {
  106. id vs = objc_msgSend(pi,
  107. sel_registerName("operatingSystemVersionString"));
  108. const char *version = (const char*)objc_msgSend(vs, UTF8String);
  109. blog(LOG_INFO, "OS Version: %s", version ? version : "Unknown");
  110. }
  111. static void log_os(void)
  112. {
  113. Class NSProcessInfo = objc_getClass("NSProcessInfo");
  114. id pi = objc_msgSend((id)NSProcessInfo,
  115. sel_registerName("processInfo"));
  116. SEL UTF8String = sel_registerName("UTF8String");
  117. log_os_name(pi, UTF8String);
  118. log_os_version(pi, UTF8String);
  119. }
  120. static void log_kernel_version(void)
  121. {
  122. char kernel_version[1024];
  123. size_t size = sizeof(kernel_version);
  124. int ret;
  125. ret = sysctlbyname("kern.osrelease", kernel_version, &size,
  126. NULL, 0);
  127. if (ret == 0)
  128. blog(LOG_INFO, "Kernel Version: %s", kernel_version);
  129. }
  130. void log_system_info(void)
  131. {
  132. log_processor_name();
  133. log_processor_speed();
  134. log_processor_cores();
  135. log_available_memory();
  136. log_os();
  137. log_kernel_version();
  138. }
  139. static bool dstr_from_cfstring(struct dstr *str, CFStringRef ref)
  140. {
  141. CFIndex length = CFStringGetLength(ref);
  142. CFIndex max_size = CFStringGetMaximumSizeForEncoding(length,
  143. kCFStringEncodingUTF8);
  144. dstr_reserve(str, max_size);
  145. if (!CFStringGetCString(ref, str->array, max_size,
  146. kCFStringEncodingUTF8))
  147. return false;
  148. str->len = strlen(str->array);
  149. return true;
  150. }
  151. struct obs_hotkeys_platform {
  152. volatile long refs;
  153. TISInputSourceRef tis;
  154. CFDataRef layout_data;
  155. UCKeyboardLayout *layout;
  156. IOHIDManagerRef manager;
  157. DARRAY(IOHIDElementRef) keys[OBS_KEY_LAST_VALUE];
  158. };
  159. static void hotkeys_retain(struct obs_hotkeys_platform *plat)
  160. {
  161. os_atomic_inc_long(&plat->refs);
  162. }
  163. static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat);
  164. static void hotkeys_release(struct obs_hotkeys_platform *plat)
  165. {
  166. if (os_atomic_dec_long(&plat->refs) == -1)
  167. free_hotkeys_platform(plat);
  168. }
  169. #define INVALID_KEY 0xff
  170. int obs_key_to_virtual_key(obs_key_t code)
  171. {
  172. switch (code) {
  173. case OBS_KEY_A: return kVK_ANSI_A;
  174. case OBS_KEY_B: return kVK_ANSI_B;
  175. case OBS_KEY_C: return kVK_ANSI_C;
  176. case OBS_KEY_D: return kVK_ANSI_D;
  177. case OBS_KEY_E: return kVK_ANSI_E;
  178. case OBS_KEY_F: return kVK_ANSI_F;
  179. case OBS_KEY_G: return kVK_ANSI_G;
  180. case OBS_KEY_H: return kVK_ANSI_H;
  181. case OBS_KEY_I: return kVK_ANSI_I;
  182. case OBS_KEY_J: return kVK_ANSI_J;
  183. case OBS_KEY_K: return kVK_ANSI_K;
  184. case OBS_KEY_L: return kVK_ANSI_L;
  185. case OBS_KEY_M: return kVK_ANSI_M;
  186. case OBS_KEY_N: return kVK_ANSI_N;
  187. case OBS_KEY_O: return kVK_ANSI_O;
  188. case OBS_KEY_P: return kVK_ANSI_P;
  189. case OBS_KEY_Q: return kVK_ANSI_Q;
  190. case OBS_KEY_R: return kVK_ANSI_R;
  191. case OBS_KEY_S: return kVK_ANSI_S;
  192. case OBS_KEY_T: return kVK_ANSI_T;
  193. case OBS_KEY_U: return kVK_ANSI_U;
  194. case OBS_KEY_V: return kVK_ANSI_V;
  195. case OBS_KEY_W: return kVK_ANSI_W;
  196. case OBS_KEY_X: return kVK_ANSI_X;
  197. case OBS_KEY_Y: return kVK_ANSI_Y;
  198. case OBS_KEY_Z: return kVK_ANSI_Z;
  199. case OBS_KEY_1: return kVK_ANSI_1;
  200. case OBS_KEY_2: return kVK_ANSI_2;
  201. case OBS_KEY_3: return kVK_ANSI_3;
  202. case OBS_KEY_4: return kVK_ANSI_4;
  203. case OBS_KEY_5: return kVK_ANSI_5;
  204. case OBS_KEY_6: return kVK_ANSI_6;
  205. case OBS_KEY_7: return kVK_ANSI_7;
  206. case OBS_KEY_8: return kVK_ANSI_8;
  207. case OBS_KEY_9: return kVK_ANSI_9;
  208. case OBS_KEY_0: return kVK_ANSI_0;
  209. case OBS_KEY_RETURN: return kVK_Return;
  210. case OBS_KEY_ESCAPE: return kVK_Escape;
  211. case OBS_KEY_BACKSPACE: return kVK_Delete;
  212. case OBS_KEY_TAB: return kVK_Tab;
  213. case OBS_KEY_SPACE: return kVK_Space;
  214. case OBS_KEY_MINUS: return kVK_ANSI_Minus;
  215. case OBS_KEY_EQUAL: return kVK_ANSI_Equal;
  216. case OBS_KEY_BRACKETLEFT: return kVK_ANSI_LeftBracket;
  217. case OBS_KEY_BRACKETRIGHT: return kVK_ANSI_RightBracket;
  218. case OBS_KEY_BACKSLASH: return kVK_ANSI_Backslash;
  219. case OBS_KEY_SEMICOLON: return kVK_ANSI_Semicolon;
  220. case OBS_KEY_QUOTE: return kVK_ANSI_Quote;
  221. case OBS_KEY_DEAD_GRAVE: return kVK_ANSI_Grave;
  222. case OBS_KEY_COMMA: return kVK_ANSI_Comma;
  223. case OBS_KEY_PERIOD: return kVK_ANSI_Period;
  224. case OBS_KEY_SLASH: return kVK_ANSI_Slash;
  225. case OBS_KEY_CAPSLOCK: return kVK_CapsLock;
  226. case OBS_KEY_SECTION: return kVK_ISO_Section;
  227. case OBS_KEY_F1: return kVK_F1;
  228. case OBS_KEY_F2: return kVK_F2;
  229. case OBS_KEY_F3: return kVK_F3;
  230. case OBS_KEY_F4: return kVK_F4;
  231. case OBS_KEY_F5: return kVK_F5;
  232. case OBS_KEY_F6: return kVK_F6;
  233. case OBS_KEY_F7: return kVK_F7;
  234. case OBS_KEY_F8: return kVK_F8;
  235. case OBS_KEY_F9: return kVK_F9;
  236. case OBS_KEY_F10: return kVK_F10;
  237. case OBS_KEY_F11: return kVK_F11;
  238. case OBS_KEY_F12: return kVK_F12;
  239. case OBS_KEY_HELP: return kVK_Help;
  240. case OBS_KEY_HOME: return kVK_Home;
  241. case OBS_KEY_PAGEUP: return kVK_PageUp;
  242. case OBS_KEY_DELETE: return kVK_ForwardDelete;
  243. case OBS_KEY_END: return kVK_End;
  244. case OBS_KEY_PAGEDOWN: return kVK_PageDown;
  245. case OBS_KEY_RIGHT: return kVK_RightArrow;
  246. case OBS_KEY_LEFT: return kVK_LeftArrow;
  247. case OBS_KEY_DOWN: return kVK_DownArrow;
  248. case OBS_KEY_UP: return kVK_UpArrow;
  249. case OBS_KEY_CLEAR: return kVK_ANSI_KeypadClear;
  250. case OBS_KEY_NUMSLASH: return kVK_ANSI_KeypadDivide;
  251. case OBS_KEY_NUMASTERISK: return kVK_ANSI_KeypadMultiply;
  252. case OBS_KEY_NUMMINUS: return kVK_ANSI_KeypadMinus;
  253. case OBS_KEY_NUMPLUS: return kVK_ANSI_KeypadPlus;
  254. case OBS_KEY_ENTER: return kVK_ANSI_KeypadEnter;
  255. case OBS_KEY_NUM1: return kVK_ANSI_Keypad1;
  256. case OBS_KEY_NUM2: return kVK_ANSI_Keypad2;
  257. case OBS_KEY_NUM3: return kVK_ANSI_Keypad3;
  258. case OBS_KEY_NUM4: return kVK_ANSI_Keypad4;
  259. case OBS_KEY_NUM5: return kVK_ANSI_Keypad5;
  260. case OBS_KEY_NUM6: return kVK_ANSI_Keypad6;
  261. case OBS_KEY_NUM7: return kVK_ANSI_Keypad7;
  262. case OBS_KEY_NUM8: return kVK_ANSI_Keypad8;
  263. case OBS_KEY_NUM9: return kVK_ANSI_Keypad9;
  264. case OBS_KEY_NUM0: return kVK_ANSI_Keypad0;
  265. case OBS_KEY_NUMPERIOD: return kVK_ANSI_KeypadDecimal;
  266. case OBS_KEY_NUMEQUAL: return kVK_ANSI_KeypadEquals;
  267. case OBS_KEY_F13: return kVK_F13;
  268. case OBS_KEY_F14: return kVK_F14;
  269. case OBS_KEY_F15: return kVK_F15;
  270. case OBS_KEY_F16: return kVK_F16;
  271. case OBS_KEY_F17: return kVK_F17;
  272. case OBS_KEY_F18: return kVK_F18;
  273. case OBS_KEY_F19: return kVK_F19;
  274. case OBS_KEY_F20: return kVK_F20;
  275. case OBS_KEY_CONTROL: return kVK_Control;
  276. case OBS_KEY_SHIFT: return kVK_Shift;
  277. case OBS_KEY_ALT: return kVK_Option;
  278. case OBS_KEY_META: return kVK_Command;
  279. //case OBS_KEY_CONTROL: return kVK_RightControl;
  280. //case OBS_KEY_SHIFT: return kVK_RightShift;
  281. //case OBS_KEY_ALT: return kVK_RightOption;
  282. //case OBS_KEY_META: return 0x36;
  283. case OBS_KEY_NONE:
  284. case OBS_KEY_LAST_VALUE:
  285. default:
  286. break;
  287. }
  288. return INVALID_KEY;
  289. }
  290. static bool localized_key_to_str(obs_key_t key, struct dstr *str)
  291. {
  292. #define MAP_KEY(k, s) case k: \
  293. dstr_copy(str, obs_get_hotkey_translation(k, s)); \
  294. return true
  295. #define MAP_BUTTON(i) case OBS_KEY_MOUSE ## i: \
  296. dstr_copy(str, obs_get_hotkey_translation(key, "Mouse " #i)); \
  297. return true
  298. switch (key) {
  299. MAP_KEY(OBS_KEY_SPACE, "Space");
  300. MAP_KEY(OBS_KEY_NUMEQUAL, "= (Keypad)");
  301. MAP_KEY(OBS_KEY_NUMASTERISK, "* (Keypad)");
  302. MAP_KEY(OBS_KEY_NUMPLUS, "+ (Keypad)");
  303. MAP_KEY(OBS_KEY_NUMMINUS, "- (Keypad)");
  304. MAP_KEY(OBS_KEY_NUMPERIOD, ". (Keypad)");
  305. MAP_KEY(OBS_KEY_NUMSLASH, "/ (Keypad)");
  306. MAP_KEY(OBS_KEY_NUM0, "0 (Keypad)");
  307. MAP_KEY(OBS_KEY_NUM1, "1 (Keypad)");
  308. MAP_KEY(OBS_KEY_NUM2, "2 (Keypad)");
  309. MAP_KEY(OBS_KEY_NUM3, "3 (Keypad)");
  310. MAP_KEY(OBS_KEY_NUM4, "4 (Keypad)");
  311. MAP_KEY(OBS_KEY_NUM5, "5 (Keypad)");
  312. MAP_KEY(OBS_KEY_NUM6, "6 (Keypad)");
  313. MAP_KEY(OBS_KEY_NUM7, "7 (Keypad)");
  314. MAP_KEY(OBS_KEY_NUM8, "8 (Keypad)");
  315. MAP_KEY(OBS_KEY_NUM9, "9 (Keypad)");
  316. MAP_BUTTON(1);
  317. MAP_BUTTON(2);
  318. MAP_BUTTON(3);
  319. MAP_BUTTON(4);
  320. MAP_BUTTON(5);
  321. MAP_BUTTON(6);
  322. MAP_BUTTON(7);
  323. MAP_BUTTON(8);
  324. MAP_BUTTON(9);
  325. MAP_BUTTON(10);
  326. MAP_BUTTON(11);
  327. MAP_BUTTON(12);
  328. MAP_BUTTON(13);
  329. MAP_BUTTON(14);
  330. MAP_BUTTON(15);
  331. MAP_BUTTON(16);
  332. MAP_BUTTON(17);
  333. MAP_BUTTON(18);
  334. MAP_BUTTON(19);
  335. MAP_BUTTON(20);
  336. MAP_BUTTON(21);
  337. MAP_BUTTON(22);
  338. MAP_BUTTON(23);
  339. MAP_BUTTON(24);
  340. MAP_BUTTON(25);
  341. MAP_BUTTON(26);
  342. MAP_BUTTON(27);
  343. MAP_BUTTON(28);
  344. MAP_BUTTON(29);
  345. default: break;
  346. }
  347. #undef MAP_BUTTON
  348. #undef MAP_KEY
  349. return false;
  350. }
  351. static bool code_to_str(int code, struct dstr *str)
  352. {
  353. #define MAP_GLYPH(c, g) \
  354. case c: dstr_from_wcs(str, (wchar_t[]){g, 0}); return true
  355. #define MAP_STR(c, s) case c: dstr_copy(str, s); return true
  356. switch (code) {
  357. MAP_GLYPH(kVK_Return, 0x21A9);
  358. MAP_GLYPH(kVK_Escape, 0x238B);
  359. MAP_GLYPH(kVK_Delete, 0x232B);
  360. MAP_GLYPH(kVK_Tab, 0x21e5);
  361. MAP_GLYPH(kVK_CapsLock, 0x21EA);
  362. MAP_GLYPH(kVK_ANSI_KeypadClear, 0x2327);
  363. MAP_GLYPH(kVK_ANSI_KeypadEnter, 0x2305);
  364. MAP_GLYPH(kVK_Help, 0x003F);
  365. MAP_GLYPH(kVK_Home, 0x2196);
  366. MAP_GLYPH(kVK_PageUp, 0x21de);
  367. MAP_GLYPH(kVK_ForwardDelete, 0x2326);
  368. MAP_GLYPH(kVK_End, 0x2198);
  369. MAP_GLYPH(kVK_PageDown, 0x21df);
  370. MAP_GLYPH(kVK_RightArrow, 0x2192);
  371. MAP_GLYPH(kVK_LeftArrow, 0x2190);
  372. MAP_GLYPH(kVK_DownArrow, 0x2193);
  373. MAP_GLYPH(kVK_UpArrow, 0x2191);
  374. MAP_STR (kVK_F1, "F1");
  375. MAP_STR (kVK_F2, "F2");
  376. MAP_STR (kVK_F3, "F3");
  377. MAP_STR (kVK_F4, "F4");
  378. MAP_STR (kVK_F5, "F5");
  379. MAP_STR (kVK_F6, "F6");
  380. MAP_STR (kVK_F7, "F7");
  381. MAP_STR (kVK_F8, "F8");
  382. MAP_STR (kVK_F9, "F9");
  383. MAP_STR (kVK_F10, "F10");
  384. MAP_STR (kVK_F11, "F11");
  385. MAP_STR (kVK_F12, "F12");
  386. MAP_STR (kVK_F13, "F13");
  387. MAP_STR (kVK_F14, "F14");
  388. MAP_STR (kVK_F15, "F15");
  389. MAP_STR (kVK_F16, "F16");
  390. MAP_STR (kVK_F17, "F17");
  391. MAP_STR (kVK_F18, "F18");
  392. MAP_STR (kVK_F19, "F19");
  393. MAP_STR (kVK_F20, "F20");
  394. MAP_GLYPH(kVK_Control, kControlUnicode);
  395. MAP_GLYPH(kVK_Shift, kShiftUnicode);
  396. MAP_GLYPH(kVK_Option, kOptionUnicode);
  397. MAP_GLYPH(kVK_Command, kCommandUnicode);
  398. MAP_GLYPH(kVK_RightControl, kControlUnicode);
  399. MAP_GLYPH(kVK_RightShift, kShiftUnicode);
  400. MAP_GLYPH(kVK_RightOption, kOptionUnicode);
  401. }
  402. #undef MAP_STR
  403. #undef MAP_GLYPH
  404. return false;
  405. }
  406. void obs_key_to_str(obs_key_t key, struct dstr *str)
  407. {
  408. if (localized_key_to_str(key, str))
  409. return;
  410. int code = obs_key_to_virtual_key(key);
  411. if (code_to_str(code, str))
  412. return;
  413. if (code == INVALID_KEY) {
  414. blog(LOG_ERROR, "hotkey-cocoa: Got invalid key while "
  415. "translating key '%d' (%s)",
  416. key, obs_key_to_name(key));
  417. goto err;
  418. }
  419. struct obs_hotkeys_platform *plat = NULL;
  420. if (obs) {
  421. pthread_mutex_lock(&obs->hotkeys.mutex);
  422. plat = obs->hotkeys.platform_context;
  423. hotkeys_retain(plat);
  424. pthread_mutex_unlock(&obs->hotkeys.mutex);
  425. }
  426. if (!plat) {
  427. blog(LOG_ERROR, "hotkey-cocoa: Could not get hotkey platform "
  428. "while translating key '%d' (%s)",
  429. key, obs_key_to_name(key));
  430. goto err;
  431. }
  432. const UniCharCount max_length = 16;
  433. UInt32 dead_key_state = 0;
  434. UniChar buffer[max_length];
  435. UniCharCount len = 0;
  436. OSStatus err = UCKeyTranslate(plat->layout,
  437. code,
  438. kUCKeyActionDown,
  439. 0x104, //caps lock for upper case letters
  440. LMGetKbdType(),
  441. kUCKeyTranslateNoDeadKeysBit,
  442. &dead_key_state,
  443. max_length,
  444. &len,
  445. buffer);
  446. if (err == noErr && len <= 0 && dead_key_state) {
  447. err = UCKeyTranslate(plat->layout,
  448. kVK_Space,
  449. kUCKeyActionDown,
  450. 0x104,
  451. LMGetKbdType(),
  452. kUCKeyTranslateNoDeadKeysBit,
  453. &dead_key_state,
  454. max_length,
  455. &len,
  456. buffer);
  457. }
  458. hotkeys_release(plat);
  459. if (err != noErr) {
  460. blog(LOG_ERROR, "hotkey-cocoa: Error while translating key '%d'"
  461. " (0x%x, %s) to string: %d", key, code,
  462. obs_key_to_name(key), err);
  463. goto err;
  464. }
  465. if (len == 0) {
  466. blog(LOG_ERROR, "hotkey-cocoa: Got 0 length string while "
  467. "translating '%d' (0x%x, %s) to string",
  468. key, code, obs_key_to_name(key));
  469. goto err;
  470. }
  471. CFStringRef string = CFStringCreateWithCharactersNoCopy(NULL,
  472. buffer, len, kCFAllocatorNull);
  473. if (!string) {
  474. blog(LOG_ERROR, "hotkey-cocoa: Could not create CFStringRef "
  475. "while translating '%d' (0x%x, %s) to string",
  476. key, code, obs_key_to_name(key));
  477. goto err;
  478. }
  479. if (!dstr_from_cfstring(str, string)) {
  480. blog(LOG_ERROR, "hotkey-cocoa: Could not translate CFStringRef "
  481. "to CString while translating '%d' (0x%x, %s)",
  482. key, code, obs_key_to_name(key));
  483. goto release;
  484. }
  485. CFRelease(string);
  486. return;
  487. release:
  488. CFRelease(string);
  489. err:
  490. dstr_copy(str, obs_key_to_name(key));
  491. }
  492. #define OBS_COCOA_MODIFIER_SIZE 7
  493. static void unichar_to_utf8(const UniChar *c, char *buff)
  494. {
  495. CFStringRef string = CFStringCreateWithCharactersNoCopy(NULL, c, 2,
  496. kCFAllocatorNull);
  497. if (!string) {
  498. blog(LOG_ERROR, "hotkey-cocoa: Could not create CFStringRef "
  499. "while populating modifier strings");
  500. return;
  501. }
  502. if (!CFStringGetCString(string, buff, OBS_COCOA_MODIFIER_SIZE,
  503. kCFStringEncodingUTF8))
  504. blog(LOG_ERROR, "hotkey-cocoa: Error while populating "
  505. " modifier string with glyph %d (0x%x)",
  506. c[0], c[0]);
  507. CFRelease(string);
  508. }
  509. static char ctrl_str[OBS_COCOA_MODIFIER_SIZE];
  510. static char opt_str[OBS_COCOA_MODIFIER_SIZE];
  511. static char shift_str[OBS_COCOA_MODIFIER_SIZE];
  512. static char cmd_str[OBS_COCOA_MODIFIER_SIZE];
  513. static void init_utf_8_strings(void)
  514. {
  515. const UniChar ctrl_uni[] = {kControlUnicode, 0};
  516. const UniChar opt_uni[] = {kOptionUnicode, 0};
  517. const UniChar shift_uni[] = {kShiftUnicode, 0};
  518. const UniChar cmd_uni[] = {kCommandUnicode, 0};
  519. unichar_to_utf8(ctrl_uni, ctrl_str);
  520. unichar_to_utf8(opt_uni, opt_str);
  521. unichar_to_utf8(shift_uni, shift_str);
  522. unichar_to_utf8(cmd_uni, cmd_str);
  523. }
  524. static pthread_once_t strings_token = PTHREAD_ONCE_INIT;
  525. void obs_key_combination_to_str(obs_key_combination_t key, struct dstr *str)
  526. {
  527. struct dstr key_str = {0};
  528. if (key.key != OBS_KEY_NONE)
  529. obs_key_to_str(key.key, &key_str);
  530. int res = pthread_once(&strings_token, init_utf_8_strings);
  531. if (res) {
  532. blog(LOG_ERROR, "hotkeys-cocoa: Error while translating "
  533. "modifiers %d (0x%x)", res, res);
  534. dstr_move(str, &key_str);
  535. return;
  536. }
  537. #define CHECK_MODIFIER(mod, str) ((key.modifiers & mod) ? str : "")
  538. dstr_printf(str, "%s%s%s%s%s",
  539. CHECK_MODIFIER(INTERACT_CONTROL_KEY, ctrl_str),
  540. CHECK_MODIFIER(INTERACT_ALT_KEY, opt_str),
  541. CHECK_MODIFIER(INTERACT_SHIFT_KEY, shift_str),
  542. CHECK_MODIFIER(INTERACT_COMMAND_KEY, cmd_str),
  543. key_str.len ? key_str.array : "");
  544. #undef CHECK_MODIFIER
  545. dstr_free(&key_str);
  546. }
  547. static inline CFDictionaryRef copy_device_mask(UInt32 page, UInt32 usage)
  548. {
  549. CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
  550. kCFAllocatorDefault, 2,
  551. &kCFTypeDictionaryKeyCallBacks,
  552. &kCFTypeDictionaryValueCallBacks);
  553. CFNumberRef value;
  554. // Add the page value.
  555. value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
  556. CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), value);
  557. CFRelease(value);
  558. // Add the usage value (which is only valid if page value exists).
  559. value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
  560. CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), value);
  561. CFRelease(value);
  562. return dict;
  563. }
  564. static CFSetRef copy_devices(obs_hotkeys_platform_t *plat,
  565. UInt32 page, UInt32 usage)
  566. {
  567. CFDictionaryRef mask = copy_device_mask(page, usage);
  568. IOHIDManagerSetDeviceMatching(plat->manager, mask);
  569. CFRelease(mask);
  570. CFSetRef devices = IOHIDManagerCopyDevices(plat->manager);
  571. if (!devices)
  572. return NULL;
  573. if (CFSetGetCount(devices) < 1) {
  574. CFRelease(devices);
  575. return NULL;
  576. }
  577. return devices;
  578. }
  579. static UInt16 usage_to_carbon(UInt32 usage)
  580. {
  581. switch (usage)
  582. {
  583. case kHIDUsage_KeyboardErrorRollOver: return INVALID_KEY;
  584. case kHIDUsage_KeyboardPOSTFail: return INVALID_KEY;
  585. case kHIDUsage_KeyboardErrorUndefined: return INVALID_KEY;
  586. case kHIDUsage_KeyboardA: return kVK_ANSI_A;
  587. case kHIDUsage_KeyboardB: return kVK_ANSI_B;
  588. case kHIDUsage_KeyboardC: return kVK_ANSI_C;
  589. case kHIDUsage_KeyboardD: return kVK_ANSI_D;
  590. case kHIDUsage_KeyboardE: return kVK_ANSI_E;
  591. case kHIDUsage_KeyboardF: return kVK_ANSI_F;
  592. case kHIDUsage_KeyboardG: return kVK_ANSI_G;
  593. case kHIDUsage_KeyboardH: return kVK_ANSI_H;
  594. case kHIDUsage_KeyboardI: return kVK_ANSI_I;
  595. case kHIDUsage_KeyboardJ: return kVK_ANSI_J;
  596. case kHIDUsage_KeyboardK: return kVK_ANSI_K;
  597. case kHIDUsage_KeyboardL: return kVK_ANSI_L;
  598. case kHIDUsage_KeyboardM: return kVK_ANSI_M;
  599. case kHIDUsage_KeyboardN: return kVK_ANSI_N;
  600. case kHIDUsage_KeyboardO: return kVK_ANSI_O;
  601. case kHIDUsage_KeyboardP: return kVK_ANSI_P;
  602. case kHIDUsage_KeyboardQ: return kVK_ANSI_Q;
  603. case kHIDUsage_KeyboardR: return kVK_ANSI_R;
  604. case kHIDUsage_KeyboardS: return kVK_ANSI_S;
  605. case kHIDUsage_KeyboardT: return kVK_ANSI_T;
  606. case kHIDUsage_KeyboardU: return kVK_ANSI_U;
  607. case kHIDUsage_KeyboardV: return kVK_ANSI_V;
  608. case kHIDUsage_KeyboardW: return kVK_ANSI_W;
  609. case kHIDUsage_KeyboardX: return kVK_ANSI_X;
  610. case kHIDUsage_KeyboardY: return kVK_ANSI_Y;
  611. case kHIDUsage_KeyboardZ: return kVK_ANSI_Z;
  612. case kHIDUsage_Keyboard1: return kVK_ANSI_1;
  613. case kHIDUsage_Keyboard2: return kVK_ANSI_2;
  614. case kHIDUsage_Keyboard3: return kVK_ANSI_3;
  615. case kHIDUsage_Keyboard4: return kVK_ANSI_4;
  616. case kHIDUsage_Keyboard5: return kVK_ANSI_5;
  617. case kHIDUsage_Keyboard6: return kVK_ANSI_6;
  618. case kHIDUsage_Keyboard7: return kVK_ANSI_7;
  619. case kHIDUsage_Keyboard8: return kVK_ANSI_8;
  620. case kHIDUsage_Keyboard9: return kVK_ANSI_9;
  621. case kHIDUsage_Keyboard0: return kVK_ANSI_0;
  622. case kHIDUsage_KeyboardReturnOrEnter: return kVK_Return;
  623. case kHIDUsage_KeyboardEscape: return kVK_Escape;
  624. case kHIDUsage_KeyboardDeleteOrBackspace: return kVK_Delete;
  625. case kHIDUsage_KeyboardTab: return kVK_Tab;
  626. case kHIDUsage_KeyboardSpacebar: return kVK_Space;
  627. case kHIDUsage_KeyboardHyphen: return kVK_ANSI_Minus;
  628. case kHIDUsage_KeyboardEqualSign: return kVK_ANSI_Equal;
  629. case kHIDUsage_KeyboardOpenBracket: return kVK_ANSI_LeftBracket;
  630. case kHIDUsage_KeyboardCloseBracket: return kVK_ANSI_RightBracket;
  631. case kHIDUsage_KeyboardBackslash: return kVK_ANSI_Backslash;
  632. case kHIDUsage_KeyboardNonUSPound: return INVALID_KEY;
  633. case kHIDUsage_KeyboardSemicolon: return kVK_ANSI_Semicolon;
  634. case kHIDUsage_KeyboardQuote: return kVK_ANSI_Quote;
  635. case kHIDUsage_KeyboardGraveAccentAndTilde: return kVK_ANSI_Grave;
  636. case kHIDUsage_KeyboardComma: return kVK_ANSI_Comma;
  637. case kHIDUsage_KeyboardPeriod: return kVK_ANSI_Period;
  638. case kHIDUsage_KeyboardSlash: return kVK_ANSI_Slash;
  639. case kHIDUsage_KeyboardCapsLock: return kVK_CapsLock;
  640. case kHIDUsage_KeyboardF1: return kVK_F1;
  641. case kHIDUsage_KeyboardF2: return kVK_F2;
  642. case kHIDUsage_KeyboardF3: return kVK_F3;
  643. case kHIDUsage_KeyboardF4: return kVK_F4;
  644. case kHIDUsage_KeyboardF5: return kVK_F5;
  645. case kHIDUsage_KeyboardF6: return kVK_F6;
  646. case kHIDUsage_KeyboardF7: return kVK_F7;
  647. case kHIDUsage_KeyboardF8: return kVK_F8;
  648. case kHIDUsage_KeyboardF9: return kVK_F9;
  649. case kHIDUsage_KeyboardF10: return kVK_F10;
  650. case kHIDUsage_KeyboardF11: return kVK_F11;
  651. case kHIDUsage_KeyboardF12: return kVK_F12;
  652. case kHIDUsage_KeyboardPrintScreen: return INVALID_KEY;
  653. case kHIDUsage_KeyboardScrollLock: return INVALID_KEY;
  654. case kHIDUsage_KeyboardPause: return INVALID_KEY;
  655. case kHIDUsage_KeyboardInsert: return kVK_Help;
  656. case kHIDUsage_KeyboardHome: return kVK_Home;
  657. case kHIDUsage_KeyboardPageUp: return kVK_PageUp;
  658. case kHIDUsage_KeyboardDeleteForward: return kVK_ForwardDelete;
  659. case kHIDUsage_KeyboardEnd: return kVK_End;
  660. case kHIDUsage_KeyboardPageDown: return kVK_PageDown;
  661. case kHIDUsage_KeyboardRightArrow: return kVK_RightArrow;
  662. case kHIDUsage_KeyboardLeftArrow: return kVK_LeftArrow;
  663. case kHIDUsage_KeyboardDownArrow: return kVK_DownArrow;
  664. case kHIDUsage_KeyboardUpArrow: return kVK_UpArrow;
  665. case kHIDUsage_KeypadNumLock: return kVK_ANSI_KeypadClear;
  666. case kHIDUsage_KeypadSlash: return kVK_ANSI_KeypadDivide;
  667. case kHIDUsage_KeypadAsterisk: return kVK_ANSI_KeypadMultiply;
  668. case kHIDUsage_KeypadHyphen: return kVK_ANSI_KeypadMinus;
  669. case kHIDUsage_KeypadPlus: return kVK_ANSI_KeypadPlus;
  670. case kHIDUsage_KeypadEnter: return kVK_ANSI_KeypadEnter;
  671. case kHIDUsage_Keypad1: return kVK_ANSI_Keypad1;
  672. case kHIDUsage_Keypad2: return kVK_ANSI_Keypad2;
  673. case kHIDUsage_Keypad3: return kVK_ANSI_Keypad3;
  674. case kHIDUsage_Keypad4: return kVK_ANSI_Keypad4;
  675. case kHIDUsage_Keypad5: return kVK_ANSI_Keypad5;
  676. case kHIDUsage_Keypad6: return kVK_ANSI_Keypad6;
  677. case kHIDUsage_Keypad7: return kVK_ANSI_Keypad7;
  678. case kHIDUsage_Keypad8: return kVK_ANSI_Keypad8;
  679. case kHIDUsage_Keypad9: return kVK_ANSI_Keypad9;
  680. case kHIDUsage_Keypad0: return kVK_ANSI_Keypad0;
  681. case kHIDUsage_KeypadPeriod: return kVK_ANSI_KeypadDecimal;
  682. case kHIDUsage_KeyboardNonUSBackslash: return INVALID_KEY;
  683. case kHIDUsage_KeyboardApplication: return kVK_F13;
  684. case kHIDUsage_KeyboardPower: return INVALID_KEY;
  685. case kHIDUsage_KeypadEqualSign: return kVK_ANSI_KeypadEquals;
  686. case kHIDUsage_KeyboardF13: return kVK_F13;
  687. case kHIDUsage_KeyboardF14: return kVK_F14;
  688. case kHIDUsage_KeyboardF15: return kVK_F15;
  689. case kHIDUsage_KeyboardF16: return kVK_F16;
  690. case kHIDUsage_KeyboardF17: return kVK_F17;
  691. case kHIDUsage_KeyboardF18: return kVK_F18;
  692. case kHIDUsage_KeyboardF19: return kVK_F19;
  693. case kHIDUsage_KeyboardF20: return kVK_F20;
  694. case kHIDUsage_KeyboardF21: return INVALID_KEY;
  695. case kHIDUsage_KeyboardF22: return INVALID_KEY;
  696. case kHIDUsage_KeyboardF23: return INVALID_KEY;
  697. case kHIDUsage_KeyboardF24: return INVALID_KEY;
  698. case kHIDUsage_KeyboardExecute: return INVALID_KEY;
  699. case kHIDUsage_KeyboardHelp: return INVALID_KEY;
  700. case kHIDUsage_KeyboardMenu: return 0x7F;
  701. case kHIDUsage_KeyboardSelect: return kVK_ANSI_KeypadEnter;
  702. case kHIDUsage_KeyboardStop: return INVALID_KEY;
  703. case kHIDUsage_KeyboardAgain: return INVALID_KEY;
  704. case kHIDUsage_KeyboardUndo: return INVALID_KEY;
  705. case kHIDUsage_KeyboardCut: return INVALID_KEY;
  706. case kHIDUsage_KeyboardCopy: return INVALID_KEY;
  707. case kHIDUsage_KeyboardPaste: return INVALID_KEY;
  708. case kHIDUsage_KeyboardFind: return INVALID_KEY;
  709. case kHIDUsage_KeyboardMute: return kVK_Mute;
  710. case kHIDUsage_KeyboardVolumeUp: return kVK_VolumeUp;
  711. case kHIDUsage_KeyboardVolumeDown: return kVK_VolumeDown;
  712. case kHIDUsage_KeyboardLockingCapsLock: return INVALID_KEY;
  713. case kHIDUsage_KeyboardLockingNumLock: return INVALID_KEY;
  714. case kHIDUsage_KeyboardLockingScrollLock: return INVALID_KEY;
  715. case kHIDUsage_KeypadComma: return INVALID_KEY;
  716. case kHIDUsage_KeypadEqualSignAS400: return INVALID_KEY;
  717. case kHIDUsage_KeyboardInternational1: return INVALID_KEY;
  718. case kHIDUsage_KeyboardInternational2: return INVALID_KEY;
  719. case kHIDUsage_KeyboardInternational3: return INVALID_KEY;
  720. case kHIDUsage_KeyboardInternational4: return INVALID_KEY;
  721. case kHIDUsage_KeyboardInternational5: return INVALID_KEY;
  722. case kHIDUsage_KeyboardInternational6: return INVALID_KEY;
  723. case kHIDUsage_KeyboardInternational7: return INVALID_KEY;
  724. case kHIDUsage_KeyboardInternational8: return INVALID_KEY;
  725. case kHIDUsage_KeyboardInternational9: return INVALID_KEY;
  726. case kHIDUsage_KeyboardLANG1: return INVALID_KEY;
  727. case kHIDUsage_KeyboardLANG2: return INVALID_KEY;
  728. case kHIDUsage_KeyboardLANG3: return INVALID_KEY;
  729. case kHIDUsage_KeyboardLANG4: return INVALID_KEY;
  730. case kHIDUsage_KeyboardLANG5: return INVALID_KEY;
  731. case kHIDUsage_KeyboardLANG6: return INVALID_KEY;
  732. case kHIDUsage_KeyboardLANG7: return INVALID_KEY;
  733. case kHIDUsage_KeyboardLANG8: return INVALID_KEY;
  734. case kHIDUsage_KeyboardLANG9: return INVALID_KEY;
  735. case kHIDUsage_KeyboardAlternateErase: return INVALID_KEY;
  736. case kHIDUsage_KeyboardSysReqOrAttention: return INVALID_KEY;
  737. case kHIDUsage_KeyboardCancel: return INVALID_KEY;
  738. case kHIDUsage_KeyboardClear: return INVALID_KEY;
  739. case kHIDUsage_KeyboardPrior: return INVALID_KEY;
  740. case kHIDUsage_KeyboardReturn: return INVALID_KEY;
  741. case kHIDUsage_KeyboardSeparator: return INVALID_KEY;
  742. case kHIDUsage_KeyboardOut: return INVALID_KEY;
  743. case kHIDUsage_KeyboardOper: return INVALID_KEY;
  744. case kHIDUsage_KeyboardClearOrAgain: return INVALID_KEY;
  745. case kHIDUsage_KeyboardCrSelOrProps: return INVALID_KEY;
  746. case kHIDUsage_KeyboardExSel: return INVALID_KEY;
  747. /* 0xa5-0xdf Reserved */
  748. case kHIDUsage_KeyboardLeftControl: return kVK_Control;
  749. case kHIDUsage_KeyboardLeftShift: return kVK_Shift;
  750. case kHIDUsage_KeyboardLeftAlt: return kVK_Option;
  751. case kHIDUsage_KeyboardLeftGUI: return kVK_Command;
  752. case kHIDUsage_KeyboardRightControl: return kVK_RightControl;
  753. case kHIDUsage_KeyboardRightShift: return kVK_RightShift;
  754. case kHIDUsage_KeyboardRightAlt: return kVK_RightOption;
  755. case kHIDUsage_KeyboardRightGUI: return 0x36; //??
  756. /* 0xe8-0xffff Reserved */
  757. case kHIDUsage_Keyboard_Reserved: return INVALID_KEY;
  758. default: return INVALID_KEY;
  759. }
  760. return INVALID_KEY;
  761. }
  762. obs_key_t obs_key_from_virtual_key(int code)
  763. {
  764. switch (code) {
  765. case kVK_ANSI_A: return OBS_KEY_A;
  766. case kVK_ANSI_B: return OBS_KEY_B;
  767. case kVK_ANSI_C: return OBS_KEY_C;
  768. case kVK_ANSI_D: return OBS_KEY_D;
  769. case kVK_ANSI_E: return OBS_KEY_E;
  770. case kVK_ANSI_F: return OBS_KEY_F;
  771. case kVK_ANSI_G: return OBS_KEY_G;
  772. case kVK_ANSI_H: return OBS_KEY_H;
  773. case kVK_ANSI_I: return OBS_KEY_I;
  774. case kVK_ANSI_J: return OBS_KEY_J;
  775. case kVK_ANSI_K: return OBS_KEY_K;
  776. case kVK_ANSI_L: return OBS_KEY_L;
  777. case kVK_ANSI_M: return OBS_KEY_M;
  778. case kVK_ANSI_N: return OBS_KEY_N;
  779. case kVK_ANSI_O: return OBS_KEY_O;
  780. case kVK_ANSI_P: return OBS_KEY_P;
  781. case kVK_ANSI_Q: return OBS_KEY_Q;
  782. case kVK_ANSI_R: return OBS_KEY_R;
  783. case kVK_ANSI_S: return OBS_KEY_S;
  784. case kVK_ANSI_T: return OBS_KEY_T;
  785. case kVK_ANSI_U: return OBS_KEY_U;
  786. case kVK_ANSI_V: return OBS_KEY_V;
  787. case kVK_ANSI_W: return OBS_KEY_W;
  788. case kVK_ANSI_X: return OBS_KEY_X;
  789. case kVK_ANSI_Y: return OBS_KEY_Y;
  790. case kVK_ANSI_Z: return OBS_KEY_Z;
  791. case kVK_ANSI_1: return OBS_KEY_1;
  792. case kVK_ANSI_2: return OBS_KEY_2;
  793. case kVK_ANSI_3: return OBS_KEY_3;
  794. case kVK_ANSI_4: return OBS_KEY_4;
  795. case kVK_ANSI_5: return OBS_KEY_5;
  796. case kVK_ANSI_6: return OBS_KEY_6;
  797. case kVK_ANSI_7: return OBS_KEY_7;
  798. case kVK_ANSI_8: return OBS_KEY_8;
  799. case kVK_ANSI_9: return OBS_KEY_9;
  800. case kVK_ANSI_0: return OBS_KEY_0;
  801. case kVK_Return: return OBS_KEY_RETURN;
  802. case kVK_Escape: return OBS_KEY_ESCAPE;
  803. case kVK_Delete: return OBS_KEY_BACKSPACE;
  804. case kVK_Tab: return OBS_KEY_TAB;
  805. case kVK_Space: return OBS_KEY_SPACE;
  806. case kVK_ANSI_Minus: return OBS_KEY_MINUS;
  807. case kVK_ANSI_Equal: return OBS_KEY_EQUAL;
  808. case kVK_ANSI_LeftBracket: return OBS_KEY_BRACKETLEFT;
  809. case kVK_ANSI_RightBracket: return OBS_KEY_BRACKETRIGHT;
  810. case kVK_ANSI_Backslash: return OBS_KEY_BACKSLASH;
  811. case kVK_ANSI_Semicolon: return OBS_KEY_SEMICOLON;
  812. case kVK_ANSI_Quote: return OBS_KEY_QUOTE;
  813. case kVK_ANSI_Grave: return OBS_KEY_DEAD_GRAVE;
  814. case kVK_ANSI_Comma: return OBS_KEY_COMMA;
  815. case kVK_ANSI_Period: return OBS_KEY_PERIOD;
  816. case kVK_ANSI_Slash: return OBS_KEY_SLASH;
  817. case kVK_CapsLock: return OBS_KEY_CAPSLOCK;
  818. case kVK_ISO_Section: return OBS_KEY_SECTION;
  819. case kVK_F1: return OBS_KEY_F1;
  820. case kVK_F2: return OBS_KEY_F2;
  821. case kVK_F3: return OBS_KEY_F3;
  822. case kVK_F4: return OBS_KEY_F4;
  823. case kVK_F5: return OBS_KEY_F5;
  824. case kVK_F6: return OBS_KEY_F6;
  825. case kVK_F7: return OBS_KEY_F7;
  826. case kVK_F8: return OBS_KEY_F8;
  827. case kVK_F9: return OBS_KEY_F9;
  828. case kVK_F10: return OBS_KEY_F10;
  829. case kVK_F11: return OBS_KEY_F11;
  830. case kVK_F12: return OBS_KEY_F12;
  831. case kVK_Help: return OBS_KEY_HELP;
  832. case kVK_Home: return OBS_KEY_HOME;
  833. case kVK_PageUp: return OBS_KEY_PAGEUP;
  834. case kVK_ForwardDelete: return OBS_KEY_DELETE;
  835. case kVK_End: return OBS_KEY_END;
  836. case kVK_PageDown: return OBS_KEY_PAGEDOWN;
  837. case kVK_RightArrow: return OBS_KEY_RIGHT;
  838. case kVK_LeftArrow: return OBS_KEY_LEFT;
  839. case kVK_DownArrow: return OBS_KEY_DOWN;
  840. case kVK_UpArrow: return OBS_KEY_UP;
  841. case kVK_ANSI_KeypadClear: return OBS_KEY_CLEAR;
  842. case kVK_ANSI_KeypadDivide: return OBS_KEY_NUMSLASH;
  843. case kVK_ANSI_KeypadMultiply: return OBS_KEY_NUMASTERISK;
  844. case kVK_ANSI_KeypadMinus: return OBS_KEY_NUMMINUS;
  845. case kVK_ANSI_KeypadPlus: return OBS_KEY_NUMPLUS;
  846. case kVK_ANSI_KeypadEnter: return OBS_KEY_ENTER;
  847. case kVK_ANSI_Keypad1: return OBS_KEY_NUM1;
  848. case kVK_ANSI_Keypad2: return OBS_KEY_NUM2;
  849. case kVK_ANSI_Keypad3: return OBS_KEY_NUM3;
  850. case kVK_ANSI_Keypad4: return OBS_KEY_NUM4;
  851. case kVK_ANSI_Keypad5: return OBS_KEY_NUM5;
  852. case kVK_ANSI_Keypad6: return OBS_KEY_NUM6;
  853. case kVK_ANSI_Keypad7: return OBS_KEY_NUM7;
  854. case kVK_ANSI_Keypad8: return OBS_KEY_NUM8;
  855. case kVK_ANSI_Keypad9: return OBS_KEY_NUM9;
  856. case kVK_ANSI_Keypad0: return OBS_KEY_NUM0;
  857. case kVK_ANSI_KeypadDecimal: return OBS_KEY_NUMPERIOD;
  858. case kVK_ANSI_KeypadEquals: return OBS_KEY_NUMEQUAL;
  859. case kVK_F13: return OBS_KEY_F13;
  860. case kVK_F14: return OBS_KEY_F14;
  861. case kVK_F15: return OBS_KEY_F15;
  862. case kVK_F16: return OBS_KEY_F16;
  863. case kVK_F17: return OBS_KEY_F17;
  864. case kVK_F18: return OBS_KEY_F18;
  865. case kVK_F19: return OBS_KEY_F19;
  866. case kVK_F20: return OBS_KEY_F20;
  867. case kVK_Control: return OBS_KEY_CONTROL;
  868. case kVK_Shift: return OBS_KEY_SHIFT;
  869. case kVK_Option: return OBS_KEY_ALT;
  870. case kVK_Command: return OBS_KEY_META;
  871. case kVK_RightControl: return OBS_KEY_CONTROL;
  872. case kVK_RightShift: return OBS_KEY_SHIFT;
  873. case kVK_RightOption: return OBS_KEY_ALT;
  874. case 0x36: return OBS_KEY_META;
  875. case kVK_Function:
  876. case kVK_Mute:
  877. case kVK_VolumeDown:
  878. case kVK_VolumeUp:
  879. break;
  880. }
  881. return OBS_KEY_NONE;
  882. }
  883. static inline void load_key(obs_hotkeys_platform_t *plat, IOHIDElementRef key)
  884. {
  885. UInt32 usage_code = IOHIDElementGetUsage(key);
  886. UInt16 carbon_code = usage_to_carbon(usage_code);
  887. if (carbon_code == INVALID_KEY) return;
  888. obs_key_t obs_key = obs_key_from_virtual_key(carbon_code);
  889. if (obs_key == OBS_KEY_NONE)
  890. return;
  891. da_push_back(plat->keys[obs_key], &key);
  892. CFRetain(*(IOHIDElementRef*)da_end(plat->keys[obs_key]));
  893. }
  894. static inline void load_keyboard(obs_hotkeys_platform_t *plat,
  895. IOHIDDeviceRef keyboard)
  896. {
  897. CFArrayRef keys = IOHIDDeviceCopyMatchingElements(keyboard, NULL,
  898. kIOHIDOptionsTypeNone);
  899. if (!keys) {
  900. blog(LOG_ERROR, "hotkeys-cocoa: Getting keyboard keys failed");
  901. return;
  902. }
  903. CFIndex count = CFArrayGetCount(keys);
  904. if (!count) {
  905. blog(LOG_ERROR, "hotkeys-cocoa: Keyboard has no keys");
  906. CFRelease(keys);
  907. return;
  908. }
  909. for (CFIndex i = 0; i < count; i++) {
  910. IOHIDElementRef key =
  911. (IOHIDElementRef)CFArrayGetValueAtIndex(keys, i);
  912. // Skip non-matching keys elements
  913. if (IOHIDElementGetUsagePage(key) != kHIDPage_KeyboardOrKeypad)
  914. continue;
  915. load_key(plat, key);
  916. }
  917. CFRelease(keys);
  918. }
  919. static bool init_keyboard(obs_hotkeys_platform_t *plat)
  920. {
  921. CFSetRef keyboards = copy_devices(plat, kHIDPage_GenericDesktop,
  922. kHIDUsage_GD_Keyboard);
  923. if (!keyboards)
  924. return false;
  925. CFIndex count = CFSetGetCount(keyboards);
  926. CFTypeRef devices[count];
  927. CFSetGetValues(keyboards, devices);
  928. for (CFIndex i = 0; i < count; i++)
  929. load_keyboard(plat, (IOHIDDeviceRef)devices[i]);
  930. CFRelease(keyboards);
  931. return true;
  932. }
  933. static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat)
  934. {
  935. if (!plat)
  936. return;
  937. if (plat->tis) {
  938. CFRelease(plat->tis);
  939. plat->tis = NULL;
  940. }
  941. if (plat->layout_data) {
  942. CFRelease(plat->layout_data);
  943. plat->layout_data = NULL;
  944. }
  945. if (plat->manager) {
  946. CFRelease(plat->manager);
  947. plat->manager = NULL;
  948. }
  949. for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
  950. for (size_t j = 0; j < plat->keys[i].num; j++)
  951. CFRelease(plat->keys[i].array[j]);
  952. da_free(plat->keys[i]);
  953. }
  954. bfree(plat);
  955. }
  956. static bool log_layout_name(TISInputSourceRef tis)
  957. {
  958. struct dstr layout_name = {0};
  959. CFStringRef sid = (CFStringRef)TISGetInputSourceProperty(tis,
  960. kTISPropertyInputSourceID);
  961. if (!sid) {
  962. blog(LOG_ERROR, "hotkeys-cocoa: Failed getting InputSourceID");
  963. return false;
  964. }
  965. if (!dstr_from_cfstring(&layout_name, sid)) {
  966. blog(LOG_ERROR, "hotkeys-cocoa: Could not convert InputSourceID"
  967. " to CString");
  968. goto fail;
  969. }
  970. blog(LOG_INFO, "hotkeys-cocoa: Using layout '%s'", layout_name.array);
  971. dstr_free(&layout_name);
  972. return true;
  973. fail:
  974. dstr_free(&layout_name);
  975. return false;
  976. }
  977. static bool init_hotkeys_platform(obs_hotkeys_platform_t **plat_)
  978. {
  979. if (!plat_)
  980. return false;
  981. *plat_ = bzalloc(sizeof(obs_hotkeys_platform_t));
  982. obs_hotkeys_platform_t *plat = *plat_;
  983. if (!plat) {
  984. *plat_ = NULL;
  985. return false;
  986. }
  987. plat->tis = TISCopyCurrentKeyboardLayoutInputSource();
  988. plat->layout_data = (CFDataRef)TISGetInputSourceProperty(plat->tis,
  989. kTISPropertyUnicodeKeyLayoutData);
  990. if (!plat->layout_data) {
  991. blog(LOG_ERROR, "hotkeys-cocoa: Failed getting LayoutData");
  992. goto fail;
  993. }
  994. CFRetain(plat->layout_data);
  995. plat->layout = (UCKeyboardLayout*)CFDataGetBytePtr(plat->layout_data);
  996. plat->manager = IOHIDManagerCreate(kCFAllocatorDefault,
  997. kIOHIDOptionsTypeNone);
  998. IOReturn openStatus = IOHIDManagerOpen(plat->manager,
  999. kIOHIDOptionsTypeNone);
  1000. if (openStatus != kIOReturnSuccess) {
  1001. blog(LOG_ERROR, "hotkeys-cocoa: Failed opening HIDManager");
  1002. goto fail;
  1003. }
  1004. init_keyboard(plat);
  1005. return true;
  1006. fail:
  1007. hotkeys_release(plat);
  1008. *plat_ = NULL;
  1009. return false;
  1010. }
  1011. static void input_method_changed(CFNotificationCenterRef nc, void *observer,
  1012. CFStringRef name, const void *object, CFDictionaryRef user_info)
  1013. {
  1014. UNUSED_PARAMETER(nc);
  1015. UNUSED_PARAMETER(name);
  1016. UNUSED_PARAMETER(object);
  1017. UNUSED_PARAMETER(user_info);
  1018. struct obs_core_hotkeys *hotkeys = observer;
  1019. obs_hotkeys_platform_t *new_plat;
  1020. if (init_hotkeys_platform(&new_plat)) {
  1021. obs_hotkeys_platform_t *plat;
  1022. pthread_mutex_lock(&hotkeys->mutex);
  1023. plat = hotkeys->platform_context;
  1024. if (new_plat && plat &&
  1025. new_plat->layout_data == plat->layout_data) {
  1026. pthread_mutex_unlock(&hotkeys->mutex);
  1027. hotkeys_release(new_plat);
  1028. return;
  1029. }
  1030. hotkeys->platform_context = new_plat;
  1031. if (new_plat)
  1032. log_layout_name(new_plat->tis);
  1033. pthread_mutex_unlock(&hotkeys->mutex);
  1034. calldata_t params = {0};
  1035. signal_handler_signal(hotkeys->signals,
  1036. "hotkey_layout_change", &params);
  1037. if (plat)
  1038. hotkeys_release(plat);
  1039. }
  1040. }
  1041. bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
  1042. {
  1043. CFNotificationCenterAddObserver(
  1044. CFNotificationCenterGetDistributedCenter(),
  1045. hotkeys, input_method_changed,
  1046. kTISNotifySelectedKeyboardInputSourceChanged, NULL,
  1047. CFNotificationSuspensionBehaviorDeliverImmediately);
  1048. input_method_changed(NULL, hotkeys, NULL, NULL, NULL);
  1049. return hotkeys->platform_context != NULL;
  1050. }
  1051. void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
  1052. {
  1053. CFNotificationCenterRemoveEveryObserver(
  1054. CFNotificationCenterGetDistributedCenter(),
  1055. hotkeys);
  1056. hotkeys_release(hotkeys->platform_context);
  1057. }
  1058. typedef unsigned long NSUInteger;
  1059. static bool mouse_button_pressed(obs_key_t key, bool *pressed)
  1060. {
  1061. int button = 0;
  1062. switch (key) {
  1063. #define MAP_BUTTON(n) case OBS_KEY_MOUSE ## n: button = n - 1; break
  1064. MAP_BUTTON(1);
  1065. MAP_BUTTON(2);
  1066. MAP_BUTTON(3);
  1067. MAP_BUTTON(4);
  1068. MAP_BUTTON(5);
  1069. MAP_BUTTON(6);
  1070. MAP_BUTTON(7);
  1071. MAP_BUTTON(8);
  1072. MAP_BUTTON(9);
  1073. MAP_BUTTON(10);
  1074. MAP_BUTTON(11);
  1075. MAP_BUTTON(12);
  1076. MAP_BUTTON(13);
  1077. MAP_BUTTON(14);
  1078. MAP_BUTTON(15);
  1079. MAP_BUTTON(16);
  1080. MAP_BUTTON(17);
  1081. MAP_BUTTON(18);
  1082. MAP_BUTTON(19);
  1083. MAP_BUTTON(20);
  1084. MAP_BUTTON(21);
  1085. MAP_BUTTON(22);
  1086. MAP_BUTTON(23);
  1087. MAP_BUTTON(24);
  1088. MAP_BUTTON(25);
  1089. MAP_BUTTON(26);
  1090. MAP_BUTTON(27);
  1091. MAP_BUTTON(28);
  1092. MAP_BUTTON(29);
  1093. break;
  1094. #undef MAP_BUTTON
  1095. default:
  1096. return false;
  1097. }
  1098. Class NSEvent = objc_getClass("NSEvent");
  1099. SEL pressedMouseButtons = sel_registerName("pressedMouseButtons");
  1100. NSUInteger buttons = (NSUInteger)objc_msgSend((id)NSEvent,
  1101. pressedMouseButtons);
  1102. *pressed = (buttons & (1 << button)) != 0;
  1103. return true;
  1104. }
  1105. bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *plat,
  1106. obs_key_t key)
  1107. {
  1108. bool mouse_pressed = false;
  1109. if (mouse_button_pressed(key, &mouse_pressed))
  1110. return mouse_pressed;
  1111. if (!plat)
  1112. return false;
  1113. if (key >= OBS_KEY_LAST_VALUE)
  1114. return false;
  1115. for (size_t i = 0; i < plat->keys[key].num;) {
  1116. IOHIDElementRef element = plat->keys[key].array[i];
  1117. IOHIDValueRef value = 0;
  1118. IOHIDDeviceRef device = IOHIDElementGetDevice(element);
  1119. if (IOHIDDeviceGetValue(device, element, &value) !=
  1120. kIOReturnSuccess) {
  1121. i += 1;
  1122. continue;
  1123. }
  1124. if (!value) {
  1125. CFRelease(element);
  1126. da_erase(plat->keys[key], i);
  1127. continue;
  1128. }
  1129. if (IOHIDValueGetIntegerValue(value) == 1)
  1130. return true;
  1131. i += 1;
  1132. }
  1133. return false;
  1134. }