Changes of Revision 71

obs-studio.changes Changed
x
 
1
@@ -1,4 +1,44 @@
2
 -------------------------------------------------------------------
3
+Fri Dec 13 21:17:57 UTC 2019 - jimmy@boombatower.com
4
+
5
+- Update to version 24.0.5:
6
+  * libobs: Update version to 24.0.5
7
+  * CI: Don't run clang format on some submodule plugins
8
+  * Merge pull request #2010 from wanhongqing123/master
9
+  * Merge pull request #2042 from WizardCM/custom-twitch-docks
10
+  * Merge pull request #2183 from ratwithacompiler/macos-python-fix-2
11
+  * Merge pull request #2085 from ratwithacompiler/macos-python-fix
12
+  * Merge pull request #2090 from jpark37/dxgi-refresh-rate
13
+  * Merge pull request #2089 from jpark37/dxgi-driver-version
14
+  * UI: Delete cookies before connecting account
15
+  * UI: Fix bug in untested/unused function code path
16
+  * deps/media-playback: Don't use interrupt cb for local files
17
+  * deps/media-playback: Don't exit thread on AVERROR_EXIT
18
+  * deps/obs-scripting: Fix formatting
19
+  * Merge pull request #2152 from Rosuav/fix-cursor-position
20
+  * UI: Fix Twitch panels not using dark first time
21
+  * Merge pull request #1914 from YouNow/master
22
+  * Merge pull request #2140 from DevWolk/avn-obs
23
+  * Merge pull request #2045 from wolf247/master
24
+  * Merge pull request #2179 from WizardCM/remove-help-interact
25
+  * Merge pull request #2125 from DDRBoxman/appbundle
26
+  * Merge pull request #2168 from kkartaltepe/vaapi-profile-fix
27
+  * Merge pull request #2148 from eulertour/master
28
+  * Merge pull request #2146 from Fenrirthviti/recording-bitrate-fix
29
+  * libobs: Fix race condition
30
+  * Merge pull request #2147 from JohannMG/vscode-ignore
31
+  * Merge pull request #2134 from WizardCM/wasapi-samplerate
32
+  * Merge pull request #2129 from Fenrirthviti/win-blacklist-update
33
+  * Merge pull request #2131 from jpark37/input-layout-error
34
+  * Merge pull request #2128 from Xaymar/return-to-break
35
+  * Merge pull request #2121 from cg2121/fix-warning
36
+  * Merge pull request #2110 from derrod/ffmpeg-output-fix
37
+  * Merge pull request #2106 from cg2121/fix-preview-bug
38
+  * Merge pull request #2126 from Fenrirthviti/linux-ci-fix
39
+  * Merge pull request #2091 from Programatic/xshm_wrong_windows
40
+  * Merge pull request #2120 from jpark37/objc-msgsend
41
+
42
+-------------------------------------------------------------------
43
 Tue Oct 15 14:33:53 UTC 2019 - jimmy@boombatower.com
44
 
45
 - Update to version 24.0.3:
46
obs-studio.spec Changed
8
 
1
@@ -1,5 +1,5 @@
2
 Name:           obs-studio
3
-Version:        24.0.3
4
+Version:        24.0.5
5
 Release:        0
6
 Summary:        A recording/broadcasting program
7
 Group:          Productivity/Multimedia/Video/Editors and Convertors
8
_service Changed
10
 
1
@@ -1,7 +1,7 @@
2
 <services>
3
   <service name="tar_scm" mode="disabled">
4
     <param name="versionformat">@PARENT_TAG@</param>
5
-    <param name="revision">refs/tags/24.0.3</param>
6
+    <param name="revision">refs/tags/24.0.5</param>
7
     <param name="url">git://github.com/jp9000/obs-studio.git</param>
8
     <param name="scm">git</param>
9
     <param name="changesgenerate">enable</param>
10
_servicedata Changed
9
 
1
@@ -1,6 +1,6 @@
2
 <servicedata>
3
   <service name="tar_scm">
4
     <param name="url">git://github.com/jp9000/obs-studio.git</param>
5
-    <param name="changesrevision">d88a5a5a60bcdcbdde6d5dc54dc352ae8d431070</param>
6
+    <param name="changesrevision">99638ba69782bdb10531a305093bbd25e5d3baef</param>
7
   </service>
8
 </servicedata>
9
obs-studio-24.0.3.tar.xz/libobs/obs-cocoa.c Deleted
1723
 
1
@@ -1,1721 +0,0 @@
2
-/******************************************************************************
3
-    Copyright (C) 2013 by Ruwen Hahn <palana@stunned.de>
4
-
5
-    This program is free software: you can redistribute it and/or modify
6
-    it under the terms of the GNU General Public License as published by
7
-    the Free Software Foundation, either version 2 of the License, or
8
-    (at your option) any later version.
9
-
10
-    This program is distributed in the hope that it will be useful,
11
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
-    GNU General Public License for more details.
14
-
15
-    You should have received a copy of the GNU General Public License
16
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
-******************************************************************************/
18
-
19
-#include "util/platform.h"
20
-#include "util/dstr.h"
21
-#include "obs.h"
22
-#include "obs-internal.h"
23
-
24
-#include <unistd.h>
25
-#include <sys/types.h>
26
-#include <sys/sysctl.h>
27
-
28
-#include <objc/objc.h>
29
-#include <Carbon/Carbon.h>
30
-#include <IOKit/hid/IOHIDDevice.h>
31
-#include <IOKit/hid/IOHIDManager.h>
32
-
33
-const char *get_module_extension(void)
34
-{
35
-   return ".so";
36
-}
37
-
38
-static const char *module_bin[] = {
39
-   "../obs-plugins",
40
-   OBS_INSTALL_PREFIX "obs-plugins",
41
-};
42
-
43
-static const char *module_data[] = {
44
-   "../data/obs-plugins/%module%",
45
-   OBS_INSTALL_DATA_PATH "obs-plugins/%module%",
46
-};
47
-
48
-static const int module_patterns_size =
49
-   sizeof(module_bin) / sizeof(module_bin[0]);
50
-
51
-void add_default_module_paths(void)
52
-{
53
-   for (int i = 0; i < module_patterns_size; i++)
54
-       obs_add_module_path(module_bin[i], module_data[i]);
55
-}
56
-
57
-char *find_libobs_data_file(const char *file)
58
-{
59
-   struct dstr path;
60
-   dstr_init_copy(&path, OBS_INSTALL_DATA_PATH "/libobs/");
61
-   dstr_cat(&path, file);
62
-   return path.array;
63
-}
64
-
65
-static void log_processor_name(void)
66
-{
67
-   char *name = NULL;
68
-   size_t size;
69
-   int ret;
70
-
71
-   ret = sysctlbyname("machdep.cpu.brand_string", NULL, &size, NULL, 0);
72
-   if (ret != 0)
73
-       return;
74
-
75
-   name = malloc(size);
76
-
77
-   ret = sysctlbyname("machdep.cpu.brand_string", name, &size, NULL, 0);
78
-   if (ret == 0)
79
-       blog(LOG_INFO, "CPU Name: %s", name);
80
-
81
-   free(name);
82
-}
83
-
84
-static void log_processor_speed(void)
85
-{
86
-   size_t size;
87
-   long long freq;
88
-   int ret;
89
-
90
-   size = sizeof(freq);
91
-   ret = sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0);
92
-   if (ret == 0)
93
-       blog(LOG_INFO, "CPU Speed: %lldMHz", freq / 1000000);
94
-}
95
-
96
-static void log_processor_cores(void)
97
-{
98
-   blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d",
99
-        os_get_physical_cores(), os_get_logical_cores());
100
-}
101
-
102
-static void log_available_memory(void)
103
-{
104
-   size_t size;
105
-   long long memory_available;
106
-   int ret;
107
-
108
-   size = sizeof(memory_available);
109
-   ret = sysctlbyname("hw.memsize", &memory_available, &size, NULL, 0);
110
-   if (ret == 0)
111
-       blog(LOG_INFO, "Physical Memory: %lldMB Total",
112
-            memory_available / 1024 / 1024);
113
-}
114
-
115
-static void log_os_name(id pi, SEL UTF8String)
116
-{
117
-   unsigned long os_id = (unsigned long)objc_msgSend(
118
-       pi, sel_registerName("operatingSystem"));
119
-
120
-   id os = objc_msgSend(pi, sel_registerName("operatingSystemName"));
121
-   const char *name = (const char *)objc_msgSend(os, UTF8String);
122
-
123
-   if (os_id == 5 /*NSMACHOperatingSystem*/) {
124
-       blog(LOG_INFO, "OS Name: Mac OS X (%s)", name);
125
-       return;
126
-   }
127
-
128
-   blog(LOG_INFO, "OS Name: %s", name ? name : "Unknown");
129
-}
130
-
131
-static void log_os_version(id pi, SEL UTF8String)
132
-{
133
-   id vs = objc_msgSend(pi,
134
-                sel_registerName("operatingSystemVersionString"));
135
-   const char *version = (const char *)objc_msgSend(vs, UTF8String);
136
-
137
-   blog(LOG_INFO, "OS Version: %s", version ? version : "Unknown");
138
-}
139
-
140
-static void log_os(void)
141
-{
142
-   Class NSProcessInfo = objc_getClass("NSProcessInfo");
143
-   id pi = objc_msgSend((id)NSProcessInfo,
144
-                sel_registerName("processInfo"));
145
-
146
-   SEL UTF8String = sel_registerName("UTF8String");
147
-
148
-   log_os_name(pi, UTF8String);
149
-   log_os_version(pi, UTF8String);
150
-}
151
-
152
-static void log_kernel_version(void)
153
-{
154
-   char kernel_version[1024];
155
-   size_t size = sizeof(kernel_version);
156
-   int ret;
157
-
158
-   ret = sysctlbyname("kern.osrelease", kernel_version, &size, NULL, 0);
159
-   if (ret == 0)
160
-       blog(LOG_INFO, "Kernel Version: %s", kernel_version);
161
-}
162
-
163
-void log_system_info(void)
164
-{
165
-   log_processor_name();
166
-   log_processor_speed();
167
-   log_processor_cores();
168
-   log_available_memory();
169
-   log_os();
170
-   log_kernel_version();
171
-}
172
-
173
-static bool dstr_from_cfstring(struct dstr *str, CFStringRef ref)
174
-{
175
-   CFIndex length = CFStringGetLength(ref);
176
-   CFIndex max_size = CFStringGetMaximumSizeForEncoding(
177
-       length, kCFStringEncodingUTF8);
178
-   dstr_reserve(str, max_size);
179
-
180
-   if (!CFStringGetCString(ref, str->array, max_size,
181
-               kCFStringEncodingUTF8))
182
-       return false;
183
-
184
-   str->len = strlen(str->array);
185
-   return true;
186
-}
187
-
188
-struct obs_hotkeys_platform {
189
-   volatile long refs;
190
-   TISInputSourceRef tis;
191
-   CFDataRef layout_data;
192
-   UCKeyboardLayout *layout;
193
-   IOHIDManagerRef manager;
194
-   DARRAY(IOHIDElementRef) keys[OBS_KEY_LAST_VALUE];
195
-};
196
-
197
-static void hotkeys_retain(struct obs_hotkeys_platform *plat)
198
-{
199
-   os_atomic_inc_long(&plat->refs);
200
-}
201
-
202
-static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat);
203
-static void hotkeys_release(struct obs_hotkeys_platform *plat)
204
-{
205
-   if (os_atomic_dec_long(&plat->refs) == -1)
206
-       free_hotkeys_platform(plat);
207
-}
208
-
209
-#define INVALID_KEY 0xff
210
-
211
-int obs_key_to_virtual_key(obs_key_t code)
212
-{
213
-   switch (code) {
214
-   case OBS_KEY_A:
215
-       return kVK_ANSI_A;
216
-   case OBS_KEY_B:
217
-       return kVK_ANSI_B;
218
-   case OBS_KEY_C:
219
-       return kVK_ANSI_C;
220
-   case OBS_KEY_D:
221
-       return kVK_ANSI_D;
222
-   case OBS_KEY_E:
223
-       return kVK_ANSI_E;
224
-   case OBS_KEY_F:
225
-       return kVK_ANSI_F;
226
-   case OBS_KEY_G:
227
-       return kVK_ANSI_G;
228
-   case OBS_KEY_H:
229
-       return kVK_ANSI_H;
230
-   case OBS_KEY_I:
231
-       return kVK_ANSI_I;
232
-   case OBS_KEY_J:
233
-       return kVK_ANSI_J;
234
-   case OBS_KEY_K:
235
-       return kVK_ANSI_K;
236
-   case OBS_KEY_L:
237
-       return kVK_ANSI_L;
238
-   case OBS_KEY_M:
239
-       return kVK_ANSI_M;
240
-   case OBS_KEY_N:
241
-       return kVK_ANSI_N;
242
-   case OBS_KEY_O:
243
-       return kVK_ANSI_O;
244
-   case OBS_KEY_P:
245
-       return kVK_ANSI_P;
246
-   case OBS_KEY_Q:
247
-       return kVK_ANSI_Q;
248
-   case OBS_KEY_R:
249
-       return kVK_ANSI_R;
250
-   case OBS_KEY_S:
251
-       return kVK_ANSI_S;
252
-   case OBS_KEY_T:
253
-       return kVK_ANSI_T;
254
-   case OBS_KEY_U:
255
-       return kVK_ANSI_U;
256
-   case OBS_KEY_V:
257
-       return kVK_ANSI_V;
258
-   case OBS_KEY_W:
259
-       return kVK_ANSI_W;
260
-   case OBS_KEY_X:
261
-       return kVK_ANSI_X;
262
-   case OBS_KEY_Y:
263
-       return kVK_ANSI_Y;
264
-   case OBS_KEY_Z:
265
-       return kVK_ANSI_Z;
266
-
267
-   case OBS_KEY_1:
268
-       return kVK_ANSI_1;
269
-   case OBS_KEY_2:
270
-       return kVK_ANSI_2;
271
-   case OBS_KEY_3:
272
-       return kVK_ANSI_3;
273
-   case OBS_KEY_4:
274
-       return kVK_ANSI_4;
275
-   case OBS_KEY_5:
276
-       return kVK_ANSI_5;
277
-   case OBS_KEY_6:
278
-       return kVK_ANSI_6;
279
-   case OBS_KEY_7:
280
-       return kVK_ANSI_7;
281
-   case OBS_KEY_8:
282
-       return kVK_ANSI_8;
283
-   case OBS_KEY_9:
284
-       return kVK_ANSI_9;
285
-   case OBS_KEY_0:
286
-       return kVK_ANSI_0;
287
-
288
-   case OBS_KEY_RETURN:
289
-       return kVK_Return;
290
-   case OBS_KEY_ESCAPE:
291
-       return kVK_Escape;
292
-   case OBS_KEY_BACKSPACE:
293
-       return kVK_Delete;
294
-   case OBS_KEY_TAB:
295
-       return kVK_Tab;
296
-   case OBS_KEY_SPACE:
297
-       return kVK_Space;
298
-   case OBS_KEY_MINUS:
299
-       return kVK_ANSI_Minus;
300
-   case OBS_KEY_EQUAL:
301
-       return kVK_ANSI_Equal;
302
-   case OBS_KEY_BRACKETLEFT:
303
-       return kVK_ANSI_LeftBracket;
304
-   case OBS_KEY_BRACKETRIGHT:
305
-       return kVK_ANSI_RightBracket;
306
-   case OBS_KEY_BACKSLASH:
307
-       return kVK_ANSI_Backslash;
308
-   case OBS_KEY_SEMICOLON:
309
-       return kVK_ANSI_Semicolon;
310
-   case OBS_KEY_QUOTE:
311
-       return kVK_ANSI_Quote;
312
-   case OBS_KEY_DEAD_GRAVE:
313
-       return kVK_ANSI_Grave;
314
-   case OBS_KEY_COMMA:
315
-       return kVK_ANSI_Comma;
316
-   case OBS_KEY_PERIOD:
317
-       return kVK_ANSI_Period;
318
-   case OBS_KEY_SLASH:
319
-       return kVK_ANSI_Slash;
320
-   case OBS_KEY_CAPSLOCK:
321
-       return kVK_CapsLock;
322
-   case OBS_KEY_SECTION:
323
-       return kVK_ISO_Section;
324
-
325
-   case OBS_KEY_F1:
326
-       return kVK_F1;
327
-   case OBS_KEY_F2:
328
-       return kVK_F2;
329
-   case OBS_KEY_F3:
330
-       return kVK_F3;
331
-   case OBS_KEY_F4:
332
-       return kVK_F4;
333
-   case OBS_KEY_F5:
334
-       return kVK_F5;
335
-   case OBS_KEY_F6:
336
-       return kVK_F6;
337
-   case OBS_KEY_F7:
338
-       return kVK_F7;
339
-   case OBS_KEY_F8:
340
-       return kVK_F8;
341
-   case OBS_KEY_F9:
342
-       return kVK_F9;
343
-   case OBS_KEY_F10:
344
-       return kVK_F10;
345
-   case OBS_KEY_F11:
346
-       return kVK_F11;
347
-   case OBS_KEY_F12:
348
-       return kVK_F12;
349
-
350
-   case OBS_KEY_HELP:
351
-       return kVK_Help;
352
-   case OBS_KEY_HOME:
353
-       return kVK_Home;
354
-   case OBS_KEY_PAGEUP:
355
-       return kVK_PageUp;
356
-   case OBS_KEY_DELETE:
357
-       return kVK_ForwardDelete;
358
-   case OBS_KEY_END:
359
-       return kVK_End;
360
-   case OBS_KEY_PAGEDOWN:
361
-       return kVK_PageDown;
362
-
363
-   case OBS_KEY_RIGHT:
364
-       return kVK_RightArrow;
365
-   case OBS_KEY_LEFT:
366
-       return kVK_LeftArrow;
367
-   case OBS_KEY_DOWN:
368
-       return kVK_DownArrow;
369
-   case OBS_KEY_UP:
370
-       return kVK_UpArrow;
371
-
372
-   case OBS_KEY_CLEAR:
373
-       return kVK_ANSI_KeypadClear;
374
-   case OBS_KEY_NUMSLASH:
375
-       return kVK_ANSI_KeypadDivide;
376
-   case OBS_KEY_NUMASTERISK:
377
-       return kVK_ANSI_KeypadMultiply;
378
-   case OBS_KEY_NUMMINUS:
379
-       return kVK_ANSI_KeypadMinus;
380
-   case OBS_KEY_NUMPLUS:
381
-       return kVK_ANSI_KeypadPlus;
382
-   case OBS_KEY_ENTER:
383
-       return kVK_ANSI_KeypadEnter;
384
-
385
-   case OBS_KEY_NUM1:
386
-       return kVK_ANSI_Keypad1;
387
-   case OBS_KEY_NUM2:
388
-       return kVK_ANSI_Keypad2;
389
-   case OBS_KEY_NUM3:
390
-       return kVK_ANSI_Keypad3;
391
-   case OBS_KEY_NUM4:
392
-       return kVK_ANSI_Keypad4;
393
-   case OBS_KEY_NUM5:
394
-       return kVK_ANSI_Keypad5;
395
-   case OBS_KEY_NUM6:
396
-       return kVK_ANSI_Keypad6;
397
-   case OBS_KEY_NUM7:
398
-       return kVK_ANSI_Keypad7;
399
-   case OBS_KEY_NUM8:
400
-       return kVK_ANSI_Keypad8;
401
-   case OBS_KEY_NUM9:
402
-       return kVK_ANSI_Keypad9;
403
-   case OBS_KEY_NUM0:
404
-       return kVK_ANSI_Keypad0;
405
-
406
-   case OBS_KEY_NUMPERIOD:
407
-       return kVK_ANSI_KeypadDecimal;
408
-   case OBS_KEY_NUMEQUAL:
409
-       return kVK_ANSI_KeypadEquals;
410
-
411
-   case OBS_KEY_F13:
412
-       return kVK_F13;
413
-   case OBS_KEY_F14:
414
-       return kVK_F14;
415
-   case OBS_KEY_F15:
416
-       return kVK_F15;
417
-   case OBS_KEY_F16:
418
-       return kVK_F16;
419
-   case OBS_KEY_F17:
420
-       return kVK_F17;
421
-   case OBS_KEY_F18:
422
-       return kVK_F18;
423
-   case OBS_KEY_F19:
424
-       return kVK_F19;
425
-   case OBS_KEY_F20:
426
-       return kVK_F20;
427
-
428
-   case OBS_KEY_CONTROL:
429
-       return kVK_Control;
430
-   case OBS_KEY_SHIFT:
431
-       return kVK_Shift;
432
-   case OBS_KEY_ALT:
433
-       return kVK_Option;
434
-   case OBS_KEY_META:
435
-       return kVK_Command;
436
-       //case OBS_KEY_CONTROL: return kVK_RightControl;
437
-       //case OBS_KEY_SHIFT: return kVK_RightShift;
438
-       //case OBS_KEY_ALT: return kVK_RightOption;
439
-       //case OBS_KEY_META: return 0x36;
440
-
441
-   case OBS_KEY_NONE:
442
-   case OBS_KEY_LAST_VALUE:
443
-   default:
444
-       break;
445
-   }
446
-   return INVALID_KEY;
447
-}
448
-
449
-static bool localized_key_to_str(obs_key_t key, struct dstr *str)
450
-{
451
-#define MAP_KEY(k, s)                                             \
452
-   case k:                                                   \
453
-       dstr_copy(str, obs_get_hotkey_translation(k, s)); \
454
-       return true
455
-
456
-#define MAP_BUTTON(i)                                                         \
457
-   case OBS_KEY_MOUSE##i:                                                \
458
-       dstr_copy(str, obs_get_hotkey_translation(key, "Mouse " #i)); \
459
-       return true
460
-
461
-   switch (key) {
462
-       MAP_KEY(OBS_KEY_SPACE, "Space");
463
-       MAP_KEY(OBS_KEY_NUMEQUAL, "= (Keypad)");
464
-       MAP_KEY(OBS_KEY_NUMASTERISK, "* (Keypad)");
465
-       MAP_KEY(OBS_KEY_NUMPLUS, "+ (Keypad)");
466
-       MAP_KEY(OBS_KEY_NUMMINUS, "- (Keypad)");
467
-       MAP_KEY(OBS_KEY_NUMPERIOD, ". (Keypad)");
468
-       MAP_KEY(OBS_KEY_NUMSLASH, "/ (Keypad)");
469
-       MAP_KEY(OBS_KEY_NUM0, "0 (Keypad)");
470
-       MAP_KEY(OBS_KEY_NUM1, "1 (Keypad)");
471
-       MAP_KEY(OBS_KEY_NUM2, "2 (Keypad)");
472
-       MAP_KEY(OBS_KEY_NUM3, "3 (Keypad)");
473
-       MAP_KEY(OBS_KEY_NUM4, "4 (Keypad)");
474
-       MAP_KEY(OBS_KEY_NUM5, "5 (Keypad)");
475
-       MAP_KEY(OBS_KEY_NUM6, "6 (Keypad)");
476
-       MAP_KEY(OBS_KEY_NUM7, "7 (Keypad)");
477
-       MAP_KEY(OBS_KEY_NUM8, "8 (Keypad)");
478
-       MAP_KEY(OBS_KEY_NUM9, "9 (Keypad)");
479
-
480
-       MAP_BUTTON(1);
481
-       MAP_BUTTON(2);
482
-       MAP_BUTTON(3);
483
-       MAP_BUTTON(4);
484
-       MAP_BUTTON(5);
485
-       MAP_BUTTON(6);
486
-       MAP_BUTTON(7);
487
-       MAP_BUTTON(8);
488
-       MAP_BUTTON(9);
489
-       MAP_BUTTON(10);
490
-       MAP_BUTTON(11);
491
-       MAP_BUTTON(12);
492
-       MAP_BUTTON(13);
493
-       MAP_BUTTON(14);
494
-       MAP_BUTTON(15);
495
-       MAP_BUTTON(16);
496
-       MAP_BUTTON(17);
497
-       MAP_BUTTON(18);
498
-       MAP_BUTTON(19);
499
-       MAP_BUTTON(20);
500
-       MAP_BUTTON(21);
501
-       MAP_BUTTON(22);
502
-       MAP_BUTTON(23);
503
-       MAP_BUTTON(24);
504
-       MAP_BUTTON(25);
505
-       MAP_BUTTON(26);
506
-       MAP_BUTTON(27);
507
-       MAP_BUTTON(28);
508
-       MAP_BUTTON(29);
509
-   default:
510
-       break;
511
-   }
512
-#undef MAP_BUTTON
513
-#undef MAP_KEY
514
-
515
-   return false;
516
-}
517
-
518
-static bool code_to_str(int code, struct dstr *str)
519
-{
520
-#define MAP_GLYPH(c, g)                                \
521
-   case c:                                        \
522
-       dstr_from_wcs(str, (wchar_t[]){g, 0}); \
523
-       return true
524
-#define MAP_STR(c, s)              \
525
-   case c:                    \
526
-       dstr_copy(str, s); \
527
-       return true
528
-   switch (code) {
529
-       MAP_GLYPH(kVK_Return, 0x21A9);
530
-       MAP_GLYPH(kVK_Escape, 0x238B);
531
-       MAP_GLYPH(kVK_Delete, 0x232B);
532
-       MAP_GLYPH(kVK_Tab, 0x21e5);
533
-       MAP_GLYPH(kVK_CapsLock, 0x21EA);
534
-       MAP_GLYPH(kVK_ANSI_KeypadClear, 0x2327);
535
-       MAP_GLYPH(kVK_ANSI_KeypadEnter, 0x2305);
536
-       MAP_GLYPH(kVK_Help, 0x003F);
537
-       MAP_GLYPH(kVK_Home, 0x2196);
538
-       MAP_GLYPH(kVK_PageUp, 0x21de);
539
-       MAP_GLYPH(kVK_ForwardDelete, 0x2326);
540
-       MAP_GLYPH(kVK_End, 0x2198);
541
-       MAP_GLYPH(kVK_PageDown, 0x21df);
542
-
543
-       MAP_GLYPH(kVK_RightArrow, 0x2192);
544
-       MAP_GLYPH(kVK_LeftArrow, 0x2190);
545
-       MAP_GLYPH(kVK_DownArrow, 0x2193);
546
-       MAP_GLYPH(kVK_UpArrow, 0x2191);
547
-
548
-       MAP_STR(kVK_F1, "F1");
549
-       MAP_STR(kVK_F2, "F2");
550
-       MAP_STR(kVK_F3, "F3");
551
-       MAP_STR(kVK_F4, "F4");
552
-       MAP_STR(kVK_F5, "F5");
553
-       MAP_STR(kVK_F6, "F6");
554
-       MAP_STR(kVK_F7, "F7");
555
-       MAP_STR(kVK_F8, "F8");
556
-       MAP_STR(kVK_F9, "F9");
557
-       MAP_STR(kVK_F10, "F10");
558
-       MAP_STR(kVK_F11, "F11");
559
-       MAP_STR(kVK_F12, "F12");
560
-       MAP_STR(kVK_F13, "F13");
561
-       MAP_STR(kVK_F14, "F14");
562
-       MAP_STR(kVK_F15, "F15");
563
-       MAP_STR(kVK_F16, "F16");
564
-       MAP_STR(kVK_F17, "F17");
565
-       MAP_STR(kVK_F18, "F18");
566
-       MAP_STR(kVK_F19, "F19");
567
-       MAP_STR(kVK_F20, "F20");
568
-       MAP_GLYPH(kVK_Control, kControlUnicode);
569
-       MAP_GLYPH(kVK_Shift, kShiftUnicode);
570
-       MAP_GLYPH(kVK_Option, kOptionUnicode);
571
-       MAP_GLYPH(kVK_Command, kCommandUnicode);
572
-       MAP_GLYPH(kVK_RightControl, kControlUnicode);
573
-       MAP_GLYPH(kVK_RightShift, kShiftUnicode);
574
-       MAP_GLYPH(kVK_RightOption, kOptionUnicode);
575
-   }
576
-#undef MAP_STR
577
-#undef MAP_GLYPH
578
-
579
-   return false;
580
-}
581
-
582
-void obs_key_to_str(obs_key_t key, struct dstr *str)
583
-{
584
-   if (localized_key_to_str(key, str))
585
-       return;
586
-
587
-   int code = obs_key_to_virtual_key(key);
588
-   if (code_to_str(code, str))
589
-       return;
590
-
591
-   if (code == INVALID_KEY) {
592
-       blog(LOG_ERROR,
593
-            "hotkey-cocoa: Got invalid key while "
594
-            "translating key '%d' (%s)",
595
-            key, obs_key_to_name(key));
596
-       goto err;
597
-   }
598
-
599
-   struct obs_hotkeys_platform *plat = NULL;
600
-
601
-   if (obs) {
602
-       pthread_mutex_lock(&obs->hotkeys.mutex);
603
-       plat = obs->hotkeys.platform_context;
604
-       hotkeys_retain(plat);
605
-       pthread_mutex_unlock(&obs->hotkeys.mutex);
606
-   }
607
-
608
-   if (!plat) {
609
-       blog(LOG_ERROR,
610
-            "hotkey-cocoa: Could not get hotkey platform "
611
-            "while translating key '%d' (%s)",
612
-            key, obs_key_to_name(key));
613
-       goto err;
614
-   }
615
-
616
-   const UniCharCount max_length = 16;
617
-   UInt32 dead_key_state = 0;
618
-   UniChar buffer[max_length];
619
-   UniCharCount len = 0;
620
-
621
-   OSStatus err =
622
-       UCKeyTranslate(plat->layout, code, kUCKeyActionDown,
623
-                  0x104, //caps lock for upper case letters
624
-                  LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit,
625
-                  &dead_key_state, max_length, &len, buffer);
626
-
627
-   if (err == noErr && len <= 0 && dead_key_state) {
628
-       err = UCKeyTranslate(plat->layout, kVK_Space, kUCKeyActionDown,
629
-                    0x104, LMGetKbdType(),
630
-                    kUCKeyTranslateNoDeadKeysBit,
631
-                    &dead_key_state, max_length, &len, buffer);
632
-   }
633
-
634
-   hotkeys_release(plat);
635
-
636
-   if (err != noErr) {
637
-       blog(LOG_ERROR,
638
-            "hotkey-cocoa: Error while translating key '%d'"
639
-            " (0x%x, %s) to string: %d",
640
-            key, code, obs_key_to_name(key), err);
641
-       goto err;
642
-   }
643
-
644
-   if (len == 0) {
645
-       blog(LOG_ERROR,
646
-            "hotkey-cocoa: Got 0 length string while "
647
-            "translating '%d' (0x%x, %s) to string",
648
-            key, code, obs_key_to_name(key));
649
-       goto err;
650
-   }
651
-
652
-   CFStringRef string = CFStringCreateWithCharactersNoCopy(
653
-       NULL, buffer, len, kCFAllocatorNull);
654
-   if (!string) {
655
-       blog(LOG_ERROR,
656
-            "hotkey-cocoa: Could not create CFStringRef "
657
-            "while translating '%d' (0x%x, %s) to string",
658
-            key, code, obs_key_to_name(key));
659
-       goto err;
660
-   }
661
-
662
-   if (!dstr_from_cfstring(str, string)) {
663
-       blog(LOG_ERROR,
664
-            "hotkey-cocoa: Could not translate CFStringRef "
665
-            "to CString while translating '%d' (0x%x, %s)",
666
-            key, code, obs_key_to_name(key));
667
-
668
-       goto release;
669
-   }
670
-
671
-   CFRelease(string);
672
-   return;
673
-
674
-release:
675
-   CFRelease(string);
676
-err:
677
-   dstr_copy(str, obs_key_to_name(key));
678
-}
679
-
680
-#define OBS_COCOA_MODIFIER_SIZE 7
681
-static void unichar_to_utf8(const UniChar *c, char *buff)
682
-{
683
-   CFStringRef string = CFStringCreateWithCharactersNoCopy(
684
-       NULL, c, 2, kCFAllocatorNull);
685
-   if (!string) {
686
-       blog(LOG_ERROR, "hotkey-cocoa: Could not create CFStringRef "
687
-               "while populating modifier strings");
688
-       return;
689
-   }
690
-
691
-   if (!CFStringGetCString(string, buff, OBS_COCOA_MODIFIER_SIZE,
692
-               kCFStringEncodingUTF8))
693
-       blog(LOG_ERROR,
694
-            "hotkey-cocoa: Error while populating "
695
-            " modifier string with glyph %d (0x%x)",
696
-            c[0], c[0]);
697
-
698
-   CFRelease(string);
699
-}
700
-
701
-static char ctrl_str[OBS_COCOA_MODIFIER_SIZE];
702
-static char opt_str[OBS_COCOA_MODIFIER_SIZE];
703
-static char shift_str[OBS_COCOA_MODIFIER_SIZE];
704
-static char cmd_str[OBS_COCOA_MODIFIER_SIZE];
705
-static void init_utf_8_strings(void)
706
-{
707
-   const UniChar ctrl_uni[] = {kControlUnicode, 0};
708
-   const UniChar opt_uni[] = {kOptionUnicode, 0};
709
-   const UniChar shift_uni[] = {kShiftUnicode, 0};
710
-   const UniChar cmd_uni[] = {kCommandUnicode, 0};
711
-
712
-   unichar_to_utf8(ctrl_uni, ctrl_str);
713
-   unichar_to_utf8(opt_uni, opt_str);
714
-   unichar_to_utf8(shift_uni, shift_str);
715
-   unichar_to_utf8(cmd_uni, cmd_str);
716
-}
717
-
718
-static pthread_once_t strings_token = PTHREAD_ONCE_INIT;
719
-void obs_key_combination_to_str(obs_key_combination_t key, struct dstr *str)
720
-{
721
-   struct dstr key_str = {0};
722
-   if (key.key != OBS_KEY_NONE)
723
-       obs_key_to_str(key.key, &key_str);
724
-
725
-   int res = pthread_once(&strings_token, init_utf_8_strings);
726
-   if (res) {
727
-       blog(LOG_ERROR,
728
-            "hotkeys-cocoa: Error while translating "
729
-            "modifiers %d (0x%x)",
730
-            res, res);
731
-       dstr_move(str, &key_str);
732
-       return;
733
-   }
734
-
735
-#define CHECK_MODIFIER(mod, str) ((key.modifiers & mod) ? str : "")
736
-   dstr_printf(str, "%s%s%s%s%s",
737
-           CHECK_MODIFIER(INTERACT_CONTROL_KEY, ctrl_str),
738
-           CHECK_MODIFIER(INTERACT_ALT_KEY, opt_str),
739
-           CHECK_MODIFIER(INTERACT_SHIFT_KEY, shift_str),
740
-           CHECK_MODIFIER(INTERACT_COMMAND_KEY, cmd_str),
741
-           key_str.len ? key_str.array : "");
742
-#undef CHECK_MODIFIER
743
-
744
-   dstr_free(&key_str);
745
-}
746
-
747
-static inline CFDictionaryRef copy_device_mask(UInt32 page, UInt32 usage)
748
-{
749
-   CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
750
-       kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks,
751
-       &kCFTypeDictionaryValueCallBacks);
752
-
753
-   CFNumberRef value;
754
-   // Add the page value.
755
-   value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
756
-   CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), value);
757
-   CFRelease(value);
758
-
759
-   // Add the usage value (which is only valid if page value exists).
760
-   value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
761
-   CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), value);
762
-   CFRelease(value);
763
-
764
-   return dict;
765
-}
766
-
767
-static CFSetRef copy_devices(obs_hotkeys_platform_t *plat, UInt32 page,
768
-                UInt32 usage)
769
-{
770
-   CFDictionaryRef mask = copy_device_mask(page, usage);
771
-   IOHIDManagerSetDeviceMatching(plat->manager, mask);
772
-   CFRelease(mask);
773
-
774
-   CFSetRef devices = IOHIDManagerCopyDevices(plat->manager);
775
-   if (!devices)
776
-       return NULL;
777
-
778
-   if (CFSetGetCount(devices) < 1) {
779
-       CFRelease(devices);
780
-       return NULL;
781
-   }
782
-
783
-   return devices;
784
-}
785
-
786
-static UInt16 usage_to_carbon(UInt32 usage)
787
-{
788
-   switch (usage) {
789
-   case kHIDUsage_KeyboardErrorRollOver:
790
-       return INVALID_KEY;
791
-   case kHIDUsage_KeyboardPOSTFail:
792
-       return INVALID_KEY;
793
-   case kHIDUsage_KeyboardErrorUndefined:
794
-       return INVALID_KEY;
795
-
796
-   case kHIDUsage_KeyboardA:
797
-       return kVK_ANSI_A;
798
-   case kHIDUsage_KeyboardB:
799
-       return kVK_ANSI_B;
800
-   case kHIDUsage_KeyboardC:
801
-       return kVK_ANSI_C;
802
-   case kHIDUsage_KeyboardD:
803
-       return kVK_ANSI_D;
804
-   case kHIDUsage_KeyboardE:
805
-       return kVK_ANSI_E;
806
-   case kHIDUsage_KeyboardF:
807
-       return kVK_ANSI_F;
808
-   case kHIDUsage_KeyboardG:
809
-       return kVK_ANSI_G;
810
-   case kHIDUsage_KeyboardH:
811
-       return kVK_ANSI_H;
812
-   case kHIDUsage_KeyboardI:
813
-       return kVK_ANSI_I;
814
-   case kHIDUsage_KeyboardJ:
815
-       return kVK_ANSI_J;
816
-   case kHIDUsage_KeyboardK:
817
-       return kVK_ANSI_K;
818
-   case kHIDUsage_KeyboardL:
819
-       return kVK_ANSI_L;
820
-   case kHIDUsage_KeyboardM:
821
-       return kVK_ANSI_M;
822
-   case kHIDUsage_KeyboardN:
823
-       return kVK_ANSI_N;
824
-   case kHIDUsage_KeyboardO:
825
-       return kVK_ANSI_O;
826
-   case kHIDUsage_KeyboardP:
827
-       return kVK_ANSI_P;
828
-   case kHIDUsage_KeyboardQ:
829
-       return kVK_ANSI_Q;
830
-   case kHIDUsage_KeyboardR:
831
-       return kVK_ANSI_R;
832
-   case kHIDUsage_KeyboardS:
833
-       return kVK_ANSI_S;
834
-   case kHIDUsage_KeyboardT:
835
-       return kVK_ANSI_T;
836
-   case kHIDUsage_KeyboardU:
837
-       return kVK_ANSI_U;
838
-   case kHIDUsage_KeyboardV:
839
-       return kVK_ANSI_V;
840
-   case kHIDUsage_KeyboardW:
841
-       return kVK_ANSI_W;
842
-   case kHIDUsage_KeyboardX:
843
-       return kVK_ANSI_X;
844
-   case kHIDUsage_KeyboardY:
845
-       return kVK_ANSI_Y;
846
-   case kHIDUsage_KeyboardZ:
847
-       return kVK_ANSI_Z;
848
-
849
-   case kHIDUsage_Keyboard1:
850
-       return kVK_ANSI_1;
851
-   case kHIDUsage_Keyboard2:
852
-       return kVK_ANSI_2;
853
-   case kHIDUsage_Keyboard3:
854
-       return kVK_ANSI_3;
855
-   case kHIDUsage_Keyboard4:
856
-       return kVK_ANSI_4;
857
-   case kHIDUsage_Keyboard5:
858
-       return kVK_ANSI_5;
859
-   case kHIDUsage_Keyboard6:
860
-       return kVK_ANSI_6;
861
-   case kHIDUsage_Keyboard7:
862
-       return kVK_ANSI_7;
863
-   case kHIDUsage_Keyboard8:
864
-       return kVK_ANSI_8;
865
-   case kHIDUsage_Keyboard9:
866
-       return kVK_ANSI_9;
867
-   case kHIDUsage_Keyboard0:
868
-       return kVK_ANSI_0;
869
-
870
-   case kHIDUsage_KeyboardReturnOrEnter:
871
-       return kVK_Return;
872
-   case kHIDUsage_KeyboardEscape:
873
-       return kVK_Escape;
874
-   case kHIDUsage_KeyboardDeleteOrBackspace:
875
-       return kVK_Delete;
876
-   case kHIDUsage_KeyboardTab:
877
-       return kVK_Tab;
878
-   case kHIDUsage_KeyboardSpacebar:
879
-       return kVK_Space;
880
-   case kHIDUsage_KeyboardHyphen:
881
-       return kVK_ANSI_Minus;
882
-   case kHIDUsage_KeyboardEqualSign:
883
-       return kVK_ANSI_Equal;
884
-   case kHIDUsage_KeyboardOpenBracket:
885
-       return kVK_ANSI_LeftBracket;
886
-   case kHIDUsage_KeyboardCloseBracket:
887
-       return kVK_ANSI_RightBracket;
888
-   case kHIDUsage_KeyboardBackslash:
889
-       return kVK_ANSI_Backslash;
890
-   case kHIDUsage_KeyboardNonUSPound:
891
-       return INVALID_KEY;
892
-   case kHIDUsage_KeyboardSemicolon:
893
-       return kVK_ANSI_Semicolon;
894
-   case kHIDUsage_KeyboardQuote:
895
-       return kVK_ANSI_Quote;
896
-   case kHIDUsage_KeyboardGraveAccentAndTilde:
897
-       return kVK_ANSI_Grave;
898
-   case kHIDUsage_KeyboardComma:
899
-       return kVK_ANSI_Comma;
900
-   case kHIDUsage_KeyboardPeriod:
901
-       return kVK_ANSI_Period;
902
-   case kHIDUsage_KeyboardSlash:
903
-       return kVK_ANSI_Slash;
904
-   case kHIDUsage_KeyboardCapsLock:
905
-       return kVK_CapsLock;
906
-
907
-   case kHIDUsage_KeyboardF1:
908
-       return kVK_F1;
909
-   case kHIDUsage_KeyboardF2:
910
-       return kVK_F2;
911
-   case kHIDUsage_KeyboardF3:
912
-       return kVK_F3;
913
-   case kHIDUsage_KeyboardF4:
914
-       return kVK_F4;
915
-   case kHIDUsage_KeyboardF5:
916
-       return kVK_F5;
917
-   case kHIDUsage_KeyboardF6:
918
-       return kVK_F6;
919
-   case kHIDUsage_KeyboardF7:
920
-       return kVK_F7;
921
-   case kHIDUsage_KeyboardF8:
922
-       return kVK_F8;
923
-   case kHIDUsage_KeyboardF9:
924
-       return kVK_F9;
925
-   case kHIDUsage_KeyboardF10:
926
-       return kVK_F10;
927
-   case kHIDUsage_KeyboardF11:
928
-       return kVK_F11;
929
-   case kHIDUsage_KeyboardF12:
930
-       return kVK_F12;
931
-
932
-   case kHIDUsage_KeyboardPrintScreen:
933
-       return INVALID_KEY;
934
-   case kHIDUsage_KeyboardScrollLock:
935
-       return INVALID_KEY;
936
-   case kHIDUsage_KeyboardPause:
937
-       return INVALID_KEY;
938
-   case kHIDUsage_KeyboardInsert:
939
-       return kVK_Help;
940
-   case kHIDUsage_KeyboardHome:
941
-       return kVK_Home;
942
-   case kHIDUsage_KeyboardPageUp:
943
-       return kVK_PageUp;
944
-   case kHIDUsage_KeyboardDeleteForward:
945
-       return kVK_ForwardDelete;
946
-   case kHIDUsage_KeyboardEnd:
947
-       return kVK_End;
948
-   case kHIDUsage_KeyboardPageDown:
949
-       return kVK_PageDown;
950
-
951
-   case kHIDUsage_KeyboardRightArrow:
952
-       return kVK_RightArrow;
953
-   case kHIDUsage_KeyboardLeftArrow:
954
-       return kVK_LeftArrow;
955
-   case kHIDUsage_KeyboardDownArrow:
956
-       return kVK_DownArrow;
957
-   case kHIDUsage_KeyboardUpArrow:
958
-       return kVK_UpArrow;
959
-
960
-   case kHIDUsage_KeypadNumLock:
961
-       return kVK_ANSI_KeypadClear;
962
-   case kHIDUsage_KeypadSlash:
963
-       return kVK_ANSI_KeypadDivide;
964
-   case kHIDUsage_KeypadAsterisk:
965
-       return kVK_ANSI_KeypadMultiply;
966
-   case kHIDUsage_KeypadHyphen:
967
-       return kVK_ANSI_KeypadMinus;
968
-   case kHIDUsage_KeypadPlus:
969
-       return kVK_ANSI_KeypadPlus;
970
-   case kHIDUsage_KeypadEnter:
971
-       return kVK_ANSI_KeypadEnter;
972
-
973
-   case kHIDUsage_Keypad1:
974
-       return kVK_ANSI_Keypad1;
975
-   case kHIDUsage_Keypad2:
976
-       return kVK_ANSI_Keypad2;
977
-   case kHIDUsage_Keypad3:
978
-       return kVK_ANSI_Keypad3;
979
-   case kHIDUsage_Keypad4:
980
-       return kVK_ANSI_Keypad4;
981
-   case kHIDUsage_Keypad5:
982
-       return kVK_ANSI_Keypad5;
983
-   case kHIDUsage_Keypad6:
984
-       return kVK_ANSI_Keypad6;
985
-   case kHIDUsage_Keypad7:
986
-       return kVK_ANSI_Keypad7;
987
-   case kHIDUsage_Keypad8:
988
-       return kVK_ANSI_Keypad8;
989
-   case kHIDUsage_Keypad9:
990
-       return kVK_ANSI_Keypad9;
991
-   case kHIDUsage_Keypad0:
992
-       return kVK_ANSI_Keypad0;
993
-
994
-   case kHIDUsage_KeypadPeriod:
995
-       return kVK_ANSI_KeypadDecimal;
996
-   case kHIDUsage_KeyboardNonUSBackslash:
997
-       return INVALID_KEY;
998
-   case kHIDUsage_KeyboardApplication:
999
-       return kVK_F13;
1000
-   case kHIDUsage_KeyboardPower:
1001
-       return INVALID_KEY;
1002
-   case kHIDUsage_KeypadEqualSign:
1003
-       return kVK_ANSI_KeypadEquals;
1004
-
1005
-   case kHIDUsage_KeyboardF13:
1006
-       return kVK_F13;
1007
-   case kHIDUsage_KeyboardF14:
1008
-       return kVK_F14;
1009
-   case kHIDUsage_KeyboardF15:
1010
-       return kVK_F15;
1011
-   case kHIDUsage_KeyboardF16:
1012
-       return kVK_F16;
1013
-   case kHIDUsage_KeyboardF17:
1014
-       return kVK_F17;
1015
-   case kHIDUsage_KeyboardF18:
1016
-       return kVK_F18;
1017
-   case kHIDUsage_KeyboardF19:
1018
-       return kVK_F19;
1019
-   case kHIDUsage_KeyboardF20:
1020
-       return kVK_F20;
1021
-   case kHIDUsage_KeyboardF21:
1022
-       return INVALID_KEY;
1023
-   case kHIDUsage_KeyboardF22:
1024
-       return INVALID_KEY;
1025
-   case kHIDUsage_KeyboardF23:
1026
-       return INVALID_KEY;
1027
-   case kHIDUsage_KeyboardF24:
1028
-       return INVALID_KEY;
1029
-
1030
-   case kHIDUsage_KeyboardExecute:
1031
-       return INVALID_KEY;
1032
-   case kHIDUsage_KeyboardHelp:
1033
-       return INVALID_KEY;
1034
-   case kHIDUsage_KeyboardMenu:
1035
-       return 0x7F;
1036
-   case kHIDUsage_KeyboardSelect:
1037
-       return kVK_ANSI_KeypadEnter;
1038
-   case kHIDUsage_KeyboardStop:
1039
-       return INVALID_KEY;
1040
-   case kHIDUsage_KeyboardAgain:
1041
-       return INVALID_KEY;
1042
-   case kHIDUsage_KeyboardUndo:
1043
-       return INVALID_KEY;
1044
-   case kHIDUsage_KeyboardCut:
1045
-       return INVALID_KEY;
1046
-   case kHIDUsage_KeyboardCopy:
1047
-       return INVALID_KEY;
1048
-   case kHIDUsage_KeyboardPaste:
1049
-       return INVALID_KEY;
1050
-   case kHIDUsage_KeyboardFind:
1051
-       return INVALID_KEY;
1052
-
1053
-   case kHIDUsage_KeyboardMute:
1054
-       return kVK_Mute;
1055
-   case kHIDUsage_KeyboardVolumeUp:
1056
-       return kVK_VolumeUp;
1057
-   case kHIDUsage_KeyboardVolumeDown:
1058
-       return kVK_VolumeDown;
1059
-
1060
-   case kHIDUsage_KeyboardLockingCapsLock:
1061
-       return INVALID_KEY;
1062
-   case kHIDUsage_KeyboardLockingNumLock:
1063
-       return INVALID_KEY;
1064
-   case kHIDUsage_KeyboardLockingScrollLock:
1065
-       return INVALID_KEY;
1066
-
1067
-   case kHIDUsage_KeypadComma:
1068
-       return INVALID_KEY;
1069
-   case kHIDUsage_KeypadEqualSignAS400:
1070
-       return INVALID_KEY;
1071
-   case kHIDUsage_KeyboardInternational1:
1072
-       return INVALID_KEY;
1073
-   case kHIDUsage_KeyboardInternational2:
1074
-       return INVALID_KEY;
1075
-   case kHIDUsage_KeyboardInternational3:
1076
-       return INVALID_KEY;
1077
-   case kHIDUsage_KeyboardInternational4:
1078
-       return INVALID_KEY;
1079
-   case kHIDUsage_KeyboardInternational5:
1080
-       return INVALID_KEY;
1081
-   case kHIDUsage_KeyboardInternational6:
1082
-       return INVALID_KEY;
1083
-   case kHIDUsage_KeyboardInternational7:
1084
-       return INVALID_KEY;
1085
-   case kHIDUsage_KeyboardInternational8:
1086
-       return INVALID_KEY;
1087
-   case kHIDUsage_KeyboardInternational9:
1088
-       return INVALID_KEY;
1089
-
1090
-   case kHIDUsage_KeyboardLANG1:
1091
-       return INVALID_KEY;
1092
-   case kHIDUsage_KeyboardLANG2:
1093
-       return INVALID_KEY;
1094
-   case kHIDUsage_KeyboardLANG3:
1095
-       return INVALID_KEY;
1096
-   case kHIDUsage_KeyboardLANG4:
1097
-       return INVALID_KEY;
1098
-   case kHIDUsage_KeyboardLANG5:
1099
-       return INVALID_KEY;
1100
-   case kHIDUsage_KeyboardLANG6:
1101
-       return INVALID_KEY;
1102
-   case kHIDUsage_KeyboardLANG7:
1103
-       return INVALID_KEY;
1104
-   case kHIDUsage_KeyboardLANG8:
1105
-       return INVALID_KEY;
1106
-   case kHIDUsage_KeyboardLANG9:
1107
-       return INVALID_KEY;
1108
-
1109
-   case kHIDUsage_KeyboardAlternateErase:
1110
-       return INVALID_KEY;
1111
-   case kHIDUsage_KeyboardSysReqOrAttention:
1112
-       return INVALID_KEY;
1113
-   case kHIDUsage_KeyboardCancel:
1114
-       return INVALID_KEY;
1115
-   case kHIDUsage_KeyboardClear:
1116
-       return INVALID_KEY;
1117
-   case kHIDUsage_KeyboardPrior:
1118
-       return INVALID_KEY;
1119
-   case kHIDUsage_KeyboardReturn:
1120
-       return INVALID_KEY;
1121
-   case kHIDUsage_KeyboardSeparator:
1122
-       return INVALID_KEY;
1123
-   case kHIDUsage_KeyboardOut:
1124
-       return INVALID_KEY;
1125
-   case kHIDUsage_KeyboardOper:
1126
-       return INVALID_KEY;
1127
-   case kHIDUsage_KeyboardClearOrAgain:
1128
-       return INVALID_KEY;
1129
-   case kHIDUsage_KeyboardCrSelOrProps:
1130
-       return INVALID_KEY;
1131
-   case kHIDUsage_KeyboardExSel:
1132
-       return INVALID_KEY;
1133
-
1134
-       /* 0xa5-0xdf Reserved */
1135
-
1136
-   case kHIDUsage_KeyboardLeftControl:
1137
-       return kVK_Control;
1138
-   case kHIDUsage_KeyboardLeftShift:
1139
-       return kVK_Shift;
1140
-   case kHIDUsage_KeyboardLeftAlt:
1141
-       return kVK_Option;
1142
-   case kHIDUsage_KeyboardLeftGUI:
1143
-       return kVK_Command;
1144
-   case kHIDUsage_KeyboardRightControl:
1145
-       return kVK_RightControl;
1146
-   case kHIDUsage_KeyboardRightShift:
1147
-       return kVK_RightShift;
1148
-   case kHIDUsage_KeyboardRightAlt:
1149
-       return kVK_RightOption;
1150
-   case kHIDUsage_KeyboardRightGUI:
1151
-       return 0x36; //??
1152
-
1153
-       /* 0xe8-0xffff Reserved */
1154
-
1155
-   case kHIDUsage_Keyboard_Reserved:
1156
-       return INVALID_KEY;
1157
-   default:
1158
-       return INVALID_KEY;
1159
-   }
1160
-   return INVALID_KEY;
1161
-}
1162
-
1163
-obs_key_t obs_key_from_virtual_key(int code)
1164
-{
1165
-   switch (code) {
1166
-   case kVK_ANSI_A:
1167
-       return OBS_KEY_A;
1168
-   case kVK_ANSI_B:
1169
-       return OBS_KEY_B;
1170
-   case kVK_ANSI_C:
1171
-       return OBS_KEY_C;
1172
-   case kVK_ANSI_D:
1173
-       return OBS_KEY_D;
1174
-   case kVK_ANSI_E:
1175
-       return OBS_KEY_E;
1176
-   case kVK_ANSI_F:
1177
-       return OBS_KEY_F;
1178
-   case kVK_ANSI_G:
1179
-       return OBS_KEY_G;
1180
-   case kVK_ANSI_H:
1181
-       return OBS_KEY_H;
1182
-   case kVK_ANSI_I:
1183
-       return OBS_KEY_I;
1184
-   case kVK_ANSI_J:
1185
-       return OBS_KEY_J;
1186
-   case kVK_ANSI_K:
1187
-       return OBS_KEY_K;
1188
-   case kVK_ANSI_L:
1189
-       return OBS_KEY_L;
1190
-   case kVK_ANSI_M:
1191
-       return OBS_KEY_M;
1192
-   case kVK_ANSI_N:
1193
-       return OBS_KEY_N;
1194
-   case kVK_ANSI_O:
1195
-       return OBS_KEY_O;
1196
-   case kVK_ANSI_P:
1197
-       return OBS_KEY_P;
1198
-   case kVK_ANSI_Q:
1199
-       return OBS_KEY_Q;
1200
-   case kVK_ANSI_R:
1201
-       return OBS_KEY_R;
1202
-   case kVK_ANSI_S:
1203
-       return OBS_KEY_S;
1204
-   case kVK_ANSI_T:
1205
-       return OBS_KEY_T;
1206
-   case kVK_ANSI_U:
1207
-       return OBS_KEY_U;
1208
-   case kVK_ANSI_V:
1209
-       return OBS_KEY_V;
1210
-   case kVK_ANSI_W:
1211
-       return OBS_KEY_W;
1212
-   case kVK_ANSI_X:
1213
-       return OBS_KEY_X;
1214
-   case kVK_ANSI_Y:
1215
-       return OBS_KEY_Y;
1216
-   case kVK_ANSI_Z:
1217
-       return OBS_KEY_Z;
1218
-
1219
-   case kVK_ANSI_1:
1220
-       return OBS_KEY_1;
1221
-   case kVK_ANSI_2:
1222
-       return OBS_KEY_2;
1223
-   case kVK_ANSI_3:
1224
-       return OBS_KEY_3;
1225
-   case kVK_ANSI_4:
1226
-       return OBS_KEY_4;
1227
-   case kVK_ANSI_5:
1228
-       return OBS_KEY_5;
1229
-   case kVK_ANSI_6:
1230
-       return OBS_KEY_6;
1231
-   case kVK_ANSI_7:
1232
-       return OBS_KEY_7;
1233
-   case kVK_ANSI_8:
1234
-       return OBS_KEY_8;
1235
-   case kVK_ANSI_9:
1236
-       return OBS_KEY_9;
1237
-   case kVK_ANSI_0:
1238
-       return OBS_KEY_0;
1239
-
1240
-   case kVK_Return:
1241
-       return OBS_KEY_RETURN;
1242
-   case kVK_Escape:
1243
-       return OBS_KEY_ESCAPE;
1244
-   case kVK_Delete:
1245
-       return OBS_KEY_BACKSPACE;
1246
-   case kVK_Tab:
1247
-       return OBS_KEY_TAB;
1248
-   case kVK_Space:
1249
-       return OBS_KEY_SPACE;
1250
-   case kVK_ANSI_Minus:
1251
-       return OBS_KEY_MINUS;
1252
-   case kVK_ANSI_Equal:
1253
-       return OBS_KEY_EQUAL;
1254
-   case kVK_ANSI_LeftBracket:
1255
-       return OBS_KEY_BRACKETLEFT;
1256
-   case kVK_ANSI_RightBracket:
1257
-       return OBS_KEY_BRACKETRIGHT;
1258
-   case kVK_ANSI_Backslash:
1259
-       return OBS_KEY_BACKSLASH;
1260
-   case kVK_ANSI_Semicolon:
1261
-       return OBS_KEY_SEMICOLON;
1262
-   case kVK_ANSI_Quote:
1263
-       return OBS_KEY_QUOTE;
1264
-   case kVK_ANSI_Grave:
1265
-       return OBS_KEY_DEAD_GRAVE;
1266
-   case kVK_ANSI_Comma:
1267
-       return OBS_KEY_COMMA;
1268
-   case kVK_ANSI_Period:
1269
-       return OBS_KEY_PERIOD;
1270
-   case kVK_ANSI_Slash:
1271
-       return OBS_KEY_SLASH;
1272
-   case kVK_CapsLock:
1273
-       return OBS_KEY_CAPSLOCK;
1274
-   case kVK_ISO_Section:
1275
-       return OBS_KEY_SECTION;
1276
-
1277
-   case kVK_F1:
1278
-       return OBS_KEY_F1;
1279
-   case kVK_F2:
1280
-       return OBS_KEY_F2;
1281
-   case kVK_F3:
1282
-       return OBS_KEY_F3;
1283
-   case kVK_F4:
1284
-       return OBS_KEY_F4;
1285
-   case kVK_F5:
1286
-       return OBS_KEY_F5;
1287
-   case kVK_F6:
1288
-       return OBS_KEY_F6;
1289
-   case kVK_F7:
1290
-       return OBS_KEY_F7;
1291
-   case kVK_F8:
1292
-       return OBS_KEY_F8;
1293
-   case kVK_F9:
1294
-       return OBS_KEY_F9;
1295
-   case kVK_F10:
1296
-       return OBS_KEY_F10;
1297
-   case kVK_F11:
1298
-       return OBS_KEY_F11;
1299
-   case kVK_F12:
1300
-       return OBS_KEY_F12;
1301
-
1302
-   case kVK_Help:
1303
-       return OBS_KEY_HELP;
1304
-   case kVK_Home:
1305
-       return OBS_KEY_HOME;
1306
-   case kVK_PageUp:
1307
-       return OBS_KEY_PAGEUP;
1308
-   case kVK_ForwardDelete:
1309
-       return OBS_KEY_DELETE;
1310
-   case kVK_End:
1311
-       return OBS_KEY_END;
1312
-   case kVK_PageDown:
1313
-       return OBS_KEY_PAGEDOWN;
1314
-
1315
-   case kVK_RightArrow:
1316
-       return OBS_KEY_RIGHT;
1317
-   case kVK_LeftArrow:
1318
-       return OBS_KEY_LEFT;
1319
-   case kVK_DownArrow:
1320
-       return OBS_KEY_DOWN;
1321
-   case kVK_UpArrow:
1322
-       return OBS_KEY_UP;
1323
-
1324
-   case kVK_ANSI_KeypadClear:
1325
-       return OBS_KEY_CLEAR;
1326
-   case kVK_ANSI_KeypadDivide:
1327
-       return OBS_KEY_NUMSLASH;
1328
-   case kVK_ANSI_KeypadMultiply:
1329
-       return OBS_KEY_NUMASTERISK;
1330
-   case kVK_ANSI_KeypadMinus:
1331
-       return OBS_KEY_NUMMINUS;
1332
-   case kVK_ANSI_KeypadPlus:
1333
-       return OBS_KEY_NUMPLUS;
1334
-   case kVK_ANSI_KeypadEnter:
1335
-       return OBS_KEY_ENTER;
1336
-
1337
-   case kVK_ANSI_Keypad1:
1338
-       return OBS_KEY_NUM1;
1339
-   case kVK_ANSI_Keypad2:
1340
-       return OBS_KEY_NUM2;
1341
-   case kVK_ANSI_Keypad3:
1342
-       return OBS_KEY_NUM3;
1343
-   case kVK_ANSI_Keypad4:
1344
-       return OBS_KEY_NUM4;
1345
-   case kVK_ANSI_Keypad5:
1346
-       return OBS_KEY_NUM5;
1347
-   case kVK_ANSI_Keypad6:
1348
-       return OBS_KEY_NUM6;
1349
-   case kVK_ANSI_Keypad7:
1350
-       return OBS_KEY_NUM7;
1351
-   case kVK_ANSI_Keypad8:
1352
-       return OBS_KEY_NUM8;
1353
-   case kVK_ANSI_Keypad9:
1354
-       return OBS_KEY_NUM9;
1355
-   case kVK_ANSI_Keypad0:
1356
-       return OBS_KEY_NUM0;
1357
-
1358
-   case kVK_ANSI_KeypadDecimal:
1359
-       return OBS_KEY_NUMPERIOD;
1360
-   case kVK_ANSI_KeypadEquals:
1361
-       return OBS_KEY_NUMEQUAL;
1362
-
1363
-   case kVK_F13:
1364
-       return OBS_KEY_F13;
1365
-   case kVK_F14:
1366
-       return OBS_KEY_F14;
1367
-   case kVK_F15:
1368
-       return OBS_KEY_F15;
1369
-   case kVK_F16:
1370
-       return OBS_KEY_F16;
1371
-   case kVK_F17:
1372
-       return OBS_KEY_F17;
1373
-   case kVK_F18:
1374
-       return OBS_KEY_F18;
1375
-   case kVK_F19:
1376
-       return OBS_KEY_F19;
1377
-   case kVK_F20:
1378
-       return OBS_KEY_F20;
1379
-
1380
-   case kVK_Control:
1381
-       return OBS_KEY_CONTROL;
1382
-   case kVK_Shift:
1383
-       return OBS_KEY_SHIFT;
1384
-   case kVK_Option:
1385
-       return OBS_KEY_ALT;
1386
-   case kVK_Command:
1387
-       return OBS_KEY_META;
1388
-   case kVK_RightControl:
1389
-       return OBS_KEY_CONTROL;
1390
-   case kVK_RightShift:
1391
-       return OBS_KEY_SHIFT;
1392
-   case kVK_RightOption:
1393
-       return OBS_KEY_ALT;
1394
-   case 0x36:
1395
-       return OBS_KEY_META;
1396
-
1397
-   case kVK_Function:
1398
-   case kVK_Mute:
1399
-   case kVK_VolumeDown:
1400
-   case kVK_VolumeUp:
1401
-       break;
1402
-   }
1403
-   return OBS_KEY_NONE;
1404
-}
1405
-
1406
-static inline void load_key(obs_hotkeys_platform_t *plat, IOHIDElementRef key)
1407
-{
1408
-   UInt32 usage_code = IOHIDElementGetUsage(key);
1409
-   UInt16 carbon_code = usage_to_carbon(usage_code);
1410
-
1411
-   if (carbon_code == INVALID_KEY)
1412
-       return;
1413
-
1414
-   obs_key_t obs_key = obs_key_from_virtual_key(carbon_code);
1415
-   if (obs_key == OBS_KEY_NONE)
1416
-       return;
1417
-
1418
-   da_push_back(plat->keys[obs_key], &key);
1419
-   CFRetain(*(IOHIDElementRef *)da_end(plat->keys[obs_key]));
1420
-}
1421
-
1422
-static inline void load_keyboard(obs_hotkeys_platform_t *plat,
1423
-                IOHIDDeviceRef keyboard)
1424
-{
1425
-   CFArrayRef keys = IOHIDDeviceCopyMatchingElements(
1426
-       keyboard, NULL, kIOHIDOptionsTypeNone);
1427
-
1428
-   if (!keys) {
1429
-       blog(LOG_ERROR, "hotkeys-cocoa: Getting keyboard keys failed");
1430
-       return;
1431
-   }
1432
-
1433
-   CFIndex count = CFArrayGetCount(keys);
1434
-   if (!count) {
1435
-       blog(LOG_ERROR, "hotkeys-cocoa: Keyboard has no keys");
1436
-       CFRelease(keys);
1437
-       return;
1438
-   }
1439
-
1440
-   for (CFIndex i = 0; i < count; i++) {
1441
-       IOHIDElementRef key =
1442
-           (IOHIDElementRef)CFArrayGetValueAtIndex(keys, i);
1443
-
1444
-       // Skip non-matching keys elements
1445
-       if (IOHIDElementGetUsagePage(key) != kHIDPage_KeyboardOrKeypad)
1446
-           continue;
1447
-
1448
-       load_key(plat, key);
1449
-   }
1450
-
1451
-   CFRelease(keys);
1452
-}
1453
-
1454
-static bool init_keyboard(obs_hotkeys_platform_t *plat)
1455
-{
1456
-   CFSetRef keyboards = copy_devices(plat, kHIDPage_GenericDesktop,
1457
-                     kHIDUsage_GD_Keyboard);
1458
-   if (!keyboards)
1459
-       return false;
1460
-
1461
-   CFIndex count = CFSetGetCount(keyboards);
1462
-
1463
-   CFTypeRef devices[count];
1464
-   CFSetGetValues(keyboards, devices);
1465
-
1466
-   for (CFIndex i = 0; i < count; i++)
1467
-       load_keyboard(plat, (IOHIDDeviceRef)devices[i]);
1468
-
1469
-   CFRelease(keyboards);
1470
-   return true;
1471
-}
1472
-
1473
-static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat)
1474
-{
1475
-   if (!plat)
1476
-       return;
1477
-
1478
-   if (plat->tis) {
1479
-       CFRelease(plat->tis);
1480
-       plat->tis = NULL;
1481
-   }
1482
-
1483
-   if (plat->layout_data) {
1484
-       CFRelease(plat->layout_data);
1485
-       plat->layout_data = NULL;
1486
-   }
1487
-
1488
-   if (plat->manager) {
1489
-       CFRelease(plat->manager);
1490
-       plat->manager = NULL;
1491
-   }
1492
-
1493
-   for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
1494
-       for (size_t j = 0; j < plat->keys[i].num; j++)
1495
-           CFRelease(plat->keys[i].array[j]);
1496
-
1497
-       da_free(plat->keys[i]);
1498
-   }
1499
-
1500
-   bfree(plat);
1501
-}
1502
-
1503
-static bool log_layout_name(TISInputSourceRef tis)
1504
-{
1505
-   struct dstr layout_name = {0};
1506
-   CFStringRef sid = (CFStringRef)TISGetInputSourceProperty(
1507
-       tis, kTISPropertyInputSourceID);
1508
-   if (!sid) {
1509
-       blog(LOG_ERROR, "hotkeys-cocoa: Failed getting InputSourceID");
1510
-       return false;
1511
-   }
1512
-
1513
-   if (!dstr_from_cfstring(&layout_name, sid)) {
1514
-       blog(LOG_ERROR, "hotkeys-cocoa: Could not convert InputSourceID"
1515
-               " to CString");
1516
-       goto fail;
1517
-   }
1518
-
1519
-   blog(LOG_INFO, "hotkeys-cocoa: Using layout '%s'", layout_name.array);
1520
-
1521
-   dstr_free(&layout_name);
1522
-   return true;
1523
-
1524
-fail:
1525
-   dstr_free(&layout_name);
1526
-   return false;
1527
-}
1528
-
1529
-static bool init_hotkeys_platform(obs_hotkeys_platform_t **plat_)
1530
-{
1531
-   if (!plat_)
1532
-       return false;
1533
-
1534
-   *plat_ = bzalloc(sizeof(obs_hotkeys_platform_t));
1535
-   obs_hotkeys_platform_t *plat = *plat_;
1536
-   if (!plat) {
1537
-       *plat_ = NULL;
1538
-       return false;
1539
-   }
1540
-
1541
-   plat->tis = TISCopyCurrentKeyboardLayoutInputSource();
1542
-   plat->layout_data = (CFDataRef)TISGetInputSourceProperty(
1543
-       plat->tis, kTISPropertyUnicodeKeyLayoutData);
1544
-
1545
-   if (!plat->layout_data) {
1546
-       blog(LOG_ERROR, "hotkeys-cocoa: Failed getting LayoutData");
1547
-       goto fail;
1548
-   }
1549
-
1550
-   CFRetain(plat->layout_data);
1551
-   plat->layout = (UCKeyboardLayout *)CFDataGetBytePtr(plat->layout_data);
1552
-
1553
-   plat->manager =
1554
-       IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
1555
-
1556
-   IOReturn openStatus =
1557
-       IOHIDManagerOpen(plat->manager, kIOHIDOptionsTypeNone);
1558
-   if (openStatus != kIOReturnSuccess) {
1559
-       blog(LOG_ERROR, "hotkeys-cocoa: Failed opening HIDManager");
1560
-       goto fail;
1561
-   }
1562
-
1563
-   init_keyboard(plat);
1564
-
1565
-   return true;
1566
-
1567
-fail:
1568
-   hotkeys_release(plat);
1569
-   *plat_ = NULL;
1570
-   return false;
1571
-}
1572
-
1573
-static void input_method_changed(CFNotificationCenterRef nc, void *observer,
1574
-                CFStringRef name, const void *object,
1575
-                CFDictionaryRef user_info)
1576
-{
1577
-   UNUSED_PARAMETER(nc);
1578
-   UNUSED_PARAMETER(name);
1579
-   UNUSED_PARAMETER(object);
1580
-   UNUSED_PARAMETER(user_info);
1581
-
1582
-   struct obs_core_hotkeys *hotkeys = observer;
1583
-   obs_hotkeys_platform_t *new_plat;
1584
-
1585
-   if (init_hotkeys_platform(&new_plat)) {
1586
-       obs_hotkeys_platform_t *plat;
1587
-
1588
-       pthread_mutex_lock(&hotkeys->mutex);
1589
-       plat = hotkeys->platform_context;
1590
-
1591
-       if (new_plat && plat &&
1592
-           new_plat->layout_data == plat->layout_data) {
1593
-           pthread_mutex_unlock(&hotkeys->mutex);
1594
-           hotkeys_release(new_plat);
1595
-           return;
1596
-       }
1597
-
1598
-       hotkeys->platform_context = new_plat;
1599
-       if (new_plat)
1600
-           log_layout_name(new_plat->tis);
1601
-       pthread_mutex_unlock(&hotkeys->mutex);
1602
-
1603
-       calldata_t params = {0};
1604
-       signal_handler_signal(hotkeys->signals, "hotkey_layout_change",
1605
-                     &params);
1606
-       if (plat)
1607
-           hotkeys_release(plat);
1608
-   }
1609
-}
1610
-
1611
-bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
1612
-{
1613
-   CFNotificationCenterAddObserver(
1614
-       CFNotificationCenterGetDistributedCenter(), hotkeys,
1615
-       input_method_changed,
1616
-       kTISNotifySelectedKeyboardInputSourceChanged, NULL,
1617
-       CFNotificationSuspensionBehaviorDeliverImmediately);
1618
-
1619
-   input_method_changed(NULL, hotkeys, NULL, NULL, NULL);
1620
-   return hotkeys->platform_context != NULL;
1621
-}
1622
-
1623
-void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
1624
-{
1625
-   CFNotificationCenterRemoveEveryObserver(
1626
-       CFNotificationCenterGetDistributedCenter(), hotkeys);
1627
-
1628
-   hotkeys_release(hotkeys->platform_context);
1629
-}
1630
-
1631
-typedef unsigned long NSUInteger;
1632
-static bool mouse_button_pressed(obs_key_t key, bool *pressed)
1633
-{
1634
-   int button = 0;
1635
-   switch (key) {
1636
-#define MAP_BUTTON(n)           \
1637
-   case OBS_KEY_MOUSE##n:  \
1638
-       button = n - 1; \
1639
-       break
1640
-       MAP_BUTTON(1);
1641
-       MAP_BUTTON(2);
1642
-       MAP_BUTTON(3);
1643
-       MAP_BUTTON(4);
1644
-       MAP_BUTTON(5);
1645
-       MAP_BUTTON(6);
1646
-       MAP_BUTTON(7);
1647
-       MAP_BUTTON(8);
1648
-       MAP_BUTTON(9);
1649
-       MAP_BUTTON(10);
1650
-       MAP_BUTTON(11);
1651
-       MAP_BUTTON(12);
1652
-       MAP_BUTTON(13);
1653
-       MAP_BUTTON(14);
1654
-       MAP_BUTTON(15);
1655
-       MAP_BUTTON(16);
1656
-       MAP_BUTTON(17);
1657
-       MAP_BUTTON(18);
1658
-       MAP_BUTTON(19);
1659
-       MAP_BUTTON(20);
1660
-       MAP_BUTTON(21);
1661
-       MAP_BUTTON(22);
1662
-       MAP_BUTTON(23);
1663
-       MAP_BUTTON(24);
1664
-       MAP_BUTTON(25);
1665
-       MAP_BUTTON(26);
1666
-       MAP_BUTTON(27);
1667
-       MAP_BUTTON(28);
1668
-       MAP_BUTTON(29);
1669
-       break;
1670
-#undef MAP_BUTTON
1671
-
1672
-   default:
1673
-       return false;
1674
-   }
1675
-
1676
-   Class NSEvent = objc_getClass("NSEvent");
1677
-   SEL pressedMouseButtons = sel_registerName("pressedMouseButtons");
1678
-   NSUInteger buttons =
1679
-       (NSUInteger)objc_msgSend((id)NSEvent, pressedMouseButtons);
1680
-
1681
-   *pressed = (buttons & (1 << button)) != 0;
1682
-   return true;
1683
-}
1684
-
1685
-bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *plat,
1686
-                    obs_key_t key)
1687
-{
1688
-   bool mouse_pressed = false;
1689
-   if (mouse_button_pressed(key, &mouse_pressed))
1690
-       return mouse_pressed;
1691
-
1692
-   if (!plat)
1693
-       return false;
1694
-
1695
-   if (key >= OBS_KEY_LAST_VALUE)
1696
-       return false;
1697
-
1698
-   for (size_t i = 0; i < plat->keys[key].num;) {
1699
-       IOHIDElementRef element = plat->keys[key].array[i];
1700
-       IOHIDValueRef value = 0;
1701
-       IOHIDDeviceRef device = IOHIDElementGetDevice(element);
1702
-
1703
-       if (IOHIDDeviceGetValue(device, element, &value) !=
1704
-           kIOReturnSuccess) {
1705
-           i += 1;
1706
-           continue;
1707
-       }
1708
-
1709
-       if (!value) {
1710
-           CFRelease(element);
1711
-           da_erase(plat->keys[key], i);
1712
-           continue;
1713
-       }
1714
-
1715
-       if (IOHIDValueGetIntegerValue(value) == 1)
1716
-           return true;
1717
-
1718
-       i += 1;
1719
-   }
1720
-
1721
-   return false;
1722
-}
1723
obs-studio-24.0.3.tar.xz/CI/before-deploy-osx.sh -> obs-studio-24.0.5.tar.xz/CI/before-deploy-osx.sh Changed
109
 
1
@@ -8,9 +8,12 @@
2
 set -e
3
 
4
 # Generate file name variables
5
+export GIT_TAG=$(git describe --abbrev=0)
6
 export GIT_HASH=$(git rev-parse --short HEAD)
7
 export FILE_DATE=$(date +%Y-%m-%d.%H-%M-%S)
8
-export FILENAME=$FILE_DATE-$GIT_HASH-$TRAVIS_BRANCH-osx.pkg
9
+export FILENAME=$FILE_DATE-$GIT_HASH-$TRAVIS_BRANCH-osx.dmg
10
+
11
+echo "git tag: $GIT_TAG"
12
 
13
 cd ./build
14
 
15
@@ -19,9 +22,9 @@
16
 mv ./rundir/RelWithDebInfo/data/obs-scripting/obslua.so ./rundir/RelWithDebInfo/bin/
17
 
18
 # Move obspython
19
-# hr "Moving OBS Python"
20
-# mv ./rundir/RelWithDebInfo/data/obs-scripting/_obspython.so ./rundir/RelWithDebInfo/bin/
21
-# mv ./rundir/RelWithDebInfo/data/obs-scripting/obspython.py ./rundir/RelWithDebInfo/bin/
22
+hr "Moving OBS Python"
23
+mv ./rundir/RelWithDebInfo/data/obs-scripting/_obspython.so ./rundir/RelWithDebInfo/bin/
24
+mv ./rundir/RelWithDebInfo/data/obs-scripting/obspython.py ./rundir/RelWithDebInfo/bin/
25
 
26
 # Package everything into a nice .app
27
 hr "Packaging .app"
28
@@ -30,32 +33,44 @@
29
   STABLE=true
30
 fi
31
 
32
-sudo python ../CI/install/osx/build_app.py --public-key ../CI/install/osx/OBSPublicDSAKey.pem --sparkle-framework ../../sparkle/Sparkle.framework --stable=$STABLE
33
+#sudo python ../CI/install/osx/build_app.py --public-key ../CI/install/osx/OBSPublicDSAKey.pem --sparkle-framework ../../sparkle/Sparkle.framework --stable=$STABLE
34
+
35
+../CI/install/osx/packageApp.sh
36
+
37
+# fix obs outputs
38
+cp /usr/local/opt/mbedtls/lib/libmbedtls.12.dylib ./OBS.app/Contents/Frameworks/
39
+cp /usr/local/opt/mbedtls/lib/libmbedcrypto.3.dylib ./OBS.app/Contents/Frameworks/
40
+cp /usr/local/opt/mbedtls/lib/libmbedx509.0.dylib ./OBS.app/Contents/Frameworks/
41
+install_name_tool -change /usr/local/opt/mbedtls/lib/libmbedtls.12.dylib @executable_path/../Frameworks/libmbedtls.12.dylib ./OBS.app/Contents/Plugins/obs-outputs.so
42
+install_name_tool -change /usr/local/opt/mbedtls/lib/libmbedcrypto.3.dylib @executable_path/../Frameworks/libmbedcrypto.3.dylib ./OBS.app/Contents/Plugins/obs-outputs.so
43
+install_name_tool -change /usr/local/opt/mbedtls/lib/libmbedx509.0.dylib @executable_path/../Frameworks/libmbedx509.0.dylib ./OBS.app/Contents/Plugins/obs-outputs.so
44
+install_name_tool -change /usr/local/opt/curl/lib/libcurl.4.dylib @executable_path/../Frameworks/libcurl.4.dylib ./OBS.app/Contents/Plugins/obs-outputs.so
45
+install_name_tool -change @rpath/libobs.0.dylib @executable_path/../Frameworks/libobs.0.dylib ./OBS.app/Contents/Plugins/obs-outputs.so
46
+
47
+# copy sparkle into the app
48
+hr "Copying Sparkle.framework"
49
+cp -r ../../sparkle/Sparkle.framework ./OBS.app/Contents/Frameworks/
50
+install_name_tool -change @rpath/Sparkle.framework/Versions/A/Sparkle @executable_path/../Frameworks/Sparkle.framework/Versions/A/Sparkle ./OBS.app/Contents/MacOS/obs
51
 
52
 # Copy Chromium embedded framework to app Frameworks directory
53
 hr "Copying Chromium Embedded Framework.framework"
54
 sudo mkdir -p OBS.app/Contents/Frameworks
55
 sudo cp -r ../../cef_binary_${CEF_BUILD_VERSION}_macosx64/Release/Chromium\ Embedded\ Framework.framework OBS.app/Contents/Frameworks/
56
-sudo install_name_tool -change \
57
-   @rpath/Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
58
-   ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
59
-   OBS.app/Contents/Resources/obs-plugins/obs-browser.so
60
-sudo install_name_tool -change \
61
-   @executable_path/../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
62
-   ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
63
-   OBS.app/Contents/Resources/obs-plugins/obs-browser.so   
64
-sudo install_name_tool -change \
65
-   @rpath/Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
66
-   ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
67
-   OBS.app/Contents/Resources/obs-plugins/obs-browser-page
68
-sudo install_name_tool -change \
69
-   @executable_path/../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
70
-   ../../Frameworks/Chromium\ Embedded\ Framework.framework/Chromium\ Embedded\ Framework \
71
-   OBS.app/Contents/Resources/obs-plugins/obs-browser-page
72
-
73
-# Package app
74
-hr "Generating .pkg"
75
-packagesbuild ../CI/install/osx/CMakeLists.pkgproj
76
+
77
+install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./OBS.app/Contents/Plugins/obs-browser.so
78
+install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Plugins/obs-browser.so
79
+install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./OBS.app/Contents/Plugins/obs-browser.so
80
+
81
+cp ../CI/install/osx/OBSPublicDSAKey.pem OBS.app/Contents/Resources
82
+
83
+# edit plist
84
+plutil -insert CFBundleVersion -string $GIT_TAG ./OBS.app/Contents/Info.plist
85
+plutil -insert CFBundleShortVersionString -string $GIT_TAG ./OBS.app/Contents/Info.plist
86
+plutil -insert OBSFeedsURL -string https://obsproject.com/osx_update/feeds.xml ./OBS.app/Contents/Info.plist
87
+plutil -insert SUFeedURL -string https://obsproject.com/osx_update/stable/updates.xml ./OBS.app/Contents/Info.plist
88
+plutil -insert SUPublicDSAKeyFile -string OBSPublicDSAKey.pem ./OBS.app/Contents/Info.plist
89
+
90
+dmgbuild -s ../CI/install/osx/settings.json "OBS" obs.dmg
91
 
92
 if [ -v "$TRAVIS" ]; then
93
    # Signing stuff
94
@@ -70,12 +85,10 @@
95
    security import ./Certificates.p12 -k build.keychain -T /usr/bin/productsign -P ""
96
    # macOS 10.12+
97
    security set-key-partition-list -S apple-tool:,apple: -s -k mysecretpassword build.keychain
98
-   hr "Signing Package"
99
-   productsign --sign 2MMRE5MTB8 ./OBS.pkg ./$FILENAME
100
-else
101
-   cp ./OBS.pkg ./$FILENAME
102
 fi
103
 
104
+cp ./OBS.dmg ./$FILENAME
105
+
106
 # Move to the folder that travis uses to upload artifacts from
107
 hr "Moving package to nightly folder for distribution"
108
 mkdir ../nightly
109
obs-studio-24.0.3.tar.xz/CI/install-dependencies-linux.sh -> obs-studio-24.0.5.tar.xz/CI/install-dependencies-linux.sh Changed
9
 
1
@@ -1,7 +1,6 @@
2
 #!/bin/sh
3
 set -ex
4
 
5
-sudo add-apt-repository ppa:jonathonf/ffmpeg-3 -y
6
 curl -L https://packagecloud.io/github/git-lfs/gpgkey | sudo apt-key add -
7
 
8
 # gets us newer clang
9
obs-studio-24.0.3.tar.xz/CI/install-dependencies-osx.sh -> obs-studio-24.0.5.tar.xz/CI/install-dependencies-osx.sh Changed
24
 
1
@@ -16,6 +16,8 @@
2
   /bin/bash -c "sudo xcode-select -s /Applications/Xcode_9.4.1.app/Contents/Developer"
3
 fi
4
 
5
+git fetch origin --tags
6
+
7
 # Leave obs-studio folder
8
 cd ../
9
 
10
@@ -28,10 +30,12 @@
11
 brew update
12
 
13
 #Base OBS Deps and ccache
14
-brew install jack speexdsp ccache mbedtls clang-format
15
+brew install jack speexdsp ccache mbedtls clang-format freetype fdk-aac
16
 brew install https://gist.githubusercontent.com/DDRBoxman/b3956fab6073335a4bf151db0dcbd4ad/raw/ed1342a8a86793ea8c10d8b4d712a654da121ace/qt.rb
17
 brew install https://gist.githubusercontent.com/DDRBoxman/4cada55c51803a2f963fa40ce55c9d3e/raw/572c67e908bfbc1bcb8c476ea77ea3935133f5b5/swig.rb
18
 
19
+pip install dmgbuild
20
+
21
 export PATH=/usr/local/opt/ccache/libexec:$PATH
22
 ccache -s || echo "CCache is not available."
23
 
24
obs-studio-24.0.5.tar.xz/CI/install/osx/Info.plist Added
32
 
1
@@ -0,0 +1,30 @@
2
+<?xml version="1.0" encoding="UTF-8"?>
3
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4
+<plist version="1.0">
5
+<dict>
6
+   <key>CFBundleIconFile</key>
7
+   <string>obs.icns</string>
8
+   <key>CFBundleName</key>
9
+   <string>OBS</string>
10
+   <key>CFBundleGetInfoString</key>
11
+   <string>OBS - Free and Open Source Streaming/Recording Software</string>
12
+   <key>CFBundleExecutable</key>
13
+   <string>OBS</string>
14
+   <key>CFBundleIdentifier</key>
15
+   <string>com.obsproject.obs-studio</string>
16
+   <key>CFBundlePackageType</key>
17
+   <string>APPL</string>
18
+   <key>CFBundleSignature</key>
19
+   <string>????</string>
20
+   <key>LSMinimumSystemVersion</key>
21
+   <string>10.8.5</string>
22
+   <key>NSHighResolutionCapable</key>
23
+   <true/>
24
+   <key>LSAppNapIsDisabled</key>
25
+   <true/>
26
+   <key>NSCameraUsageDescription</key>
27
+   <string>OBS needs to access the camera to enable camera sources to work.</string>
28
+   <key>NSMicrophoneUsageDescription</key>
29
+   <string>OBS needs to access the microphone to enable audio input.</string>
30
+</dict>
31
+</plist>
32
obs-studio-24.0.5.tar.xz/CI/install/osx/background.png Added
obs-studio-24.0.5.tar.xz/CI/install/osx/background.pxd Added
2
 
1
+(directory)
2
obs-studio-24.0.5.tar.xz/CI/install/osx/background.pxd/QuickLook Added
2
 
1
+(directory)
2
obs-studio-24.0.5.tar.xz/CI/install/osx/background.pxd/QuickLook/Icon.tiff Added
obs-studio-24.0.5.tar.xz/CI/install/osx/background.pxd/QuickLook/Preview.tiff Added
obs-studio-24.0.5.tar.xz/CI/install/osx/background.pxd/QuickLook/Thumbnail.tiff Added
obs-studio-24.0.5.tar.xz/CI/install/osx/background.pxd/data Added
2
 
1
+(directory)
2
obs-studio-24.0.5.tar.xz/CI/install/osx/background.pxd/data/556CF265-5721-4F18-BE83-8CF39483B4C2 Added
obs-studio-24.0.5.tar.xz/CI/install/osx/background.pxd/data/8CA689C3-ED2A-459E-952C-E08026CFCD07 Added
obs-studio-24.0.5.tar.xz/CI/install/osx/background.pxd/metadata.info Added
obs-studio-24.0.5.tar.xz/CI/install/osx/background.tiff Added
obs-studio-24.0.5.tar.xz/CI/install/osx/background@2x.png Added
obs-studio-24.0.5.tar.xz/CI/install/osx/buildDMG Added
3
 
1
@@ -0,0 +1,1 @@
2
+dmgbuild -s ./settings.json "OBS" obs.dmg
3
obs-studio-24.0.5.tar.xz/CI/install/osx/dylibBundler Added
obs-studio-24.0.5.tar.xz/CI/install/osx/makeRetinaBG Added
3
 
1
@@ -0,0 +1,1 @@
2
+tiffutil -cathidpicheck background.png background@2x.png -out background.tiff
3
obs-studio-24.0.5.tar.xz/CI/install/osx/obs.icns Added
obs-studio-24.0.5.tar.xz/CI/install/osx/packageApp.sh Added
69
 
1
@@ -0,0 +1,67 @@
2
+rm -rf ./OBS.app
3
+
4
+mkdir OBS.app
5
+mkdir OBS.app/Contents
6
+mkdir OBS.app/Contents/MacOS
7
+mkdir OBS.app/Contents/Plugins
8
+mkdir OBS.app/Contents/Resources
9
+
10
+cp -r rundir/RelWithDebInfo/bin/ ./OBS.app/Contents/MacOS
11
+cp -r rundir/RelWithDebInfo/data ./OBS.app/Contents/Resources
12
+cp ../CI/install/osx/obs.icns ./OBS.app/Contents/Resources
13
+cp -r rundir/RelWithDebInfo/obs-plugins/ ./OBS.app/Contents/Plugins
14
+cp ../CI/install/osx/Info.plist ./OBS.app/Contents
15
+
16
+../CI/install/osx/dylibBundler -b -cd -d ./OBS.app/Contents/Frameworks -p @executable_path/../Frameworks/ \
17
+-s ./OBS.app/Contents/MacOS \
18
+-s /usr/local/opt/mbedtls/lib/ \
19
+-x ./OBS.app/Contents/Plugins/coreaudio-encoder.so \
20
+-x ./OBS.app/Contents/Plugins/decklink-ouput-ui.so \
21
+-x ./OBS.app/Contents/Plugins/frontend-tools.so \
22
+-x ./OBS.app/Contents/Plugins/image-source.so \
23
+-x ./OBS.app/Contents/Plugins/linux-jack.so \
24
+-x ./OBS.app/Contents/Plugins/mac-avcapture.so \
25
+-x ./OBS.app/Contents/Plugins/mac-capture.so \
26
+-x ./OBS.app/Contents/Plugins/mac-decklink.so \
27
+-x ./OBS.app/Contents/Plugins/mac-syphon.so \
28
+-x ./OBS.app/Contents/Plugins/mac-vth264.so \
29
+-x ./OBS.app/Contents/Plugins/obs-browser.so \
30
+-x ./OBS.app/Contents/Plugins/obs-browser-page \
31
+-x ./OBS.app/Contents/Plugins/obs-ffmpeg.so \
32
+-x ./OBS.app/Contents/Plugins/obs-filters.so \
33
+-x ./OBS.app/Contents/Plugins/obs-transitions.so \
34
+-x ./OBS.app/Contents/Plugins/obs-vst.so \
35
+-x ./OBS.app/Contents/Plugins/rtmp-services.so \
36
+-x ./OBS.app/Contents/MacOS/obs \
37
+-x ./OBS.app/Contents/MacOS/obs-ffmpeg-mux \
38
+-x ./OBS.app/Contents/MacOS/obslua.so \
39
+-x ./OBS.app/Contents/MacOS/_obspython.so \
40
+-x ./OBS.app/Contents/Plugins/obs-x264.so \
41
+-x ./OBS.app/Contents/Plugins/text-freetype2.so \
42
+-x ./OBS.app/Contents/Plugins/obs-libfdk.so
43
+# -x ./OBS.app/Contents/Plugins/obs-outputs.so \
44
+
45
+/usr/local/Cellar/qt/5.10.1/bin/macdeployqt ./OBS.app
46
+
47
+mv ./OBS.app/Contents/MacOS/libobs-opengl.so ./OBS.app/Contents/Frameworks
48
+
49
+# put qt network in here becasuse streamdeck uses it
50
+cp -r /usr/local/opt/qt/lib/QtNetwork.framework ./OBS.app/Contents/Frameworks
51
+chmod +w ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/QtNetwork
52
+install_name_tool -change /usr/local/Cellar/qt/5.10.1/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Frameworks/QtNetwork.framework/Versions/5/QtNetwork
53
+
54
+# decklink ui qt
55
+install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./OBS.app/Contents/Plugins/decklink-ouput-ui.so
56
+install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Plugins/decklink-ouput-ui.so
57
+install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./OBS.app/Contents/Plugins/decklink-ouput-ui.so
58
+
59
+# frontend tools qt
60
+install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./OBS.app/Contents/Plugins/frontend-tools.so
61
+install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Plugins/frontend-tools.so
62
+install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./OBS.app/Contents/Plugins/frontend-tools.so
63
+
64
+# vst qt
65
+install_name_tool -change /usr/local/opt/qt/lib/QtGui.framework/Versions/5/QtGui @executable_path/../Frameworks/QtGui.framework/Versions/5/QtGui ./OBS.app/Contents/Plugins/obs-vst.so
66
+install_name_tool -change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @executable_path/../Frameworks/QtCore.framework/Versions/5/QtCore ./OBS.app/Contents/Plugins/obs-vst.so
67
+install_name_tool -change /usr/local/opt/qt/lib/QtWidgets.framework/Versions/5/QtWidgets @executable_path/../Frameworks/QtWidgets.framework/Versions/5/QtWidgets ./OBS.app/Contents/Plugins/obs-vst.so
68
+install_name_tool -change /usr/local/opt/qt/lib/QtMacExtras.framework/Versions/5/QtMacExtras @executable_path/../Frameworks/QtMacExtras.framework/Versions/5/QtMacExtras ./OBS.app/Contents/Plugins/obs-vst.so
69
obs-studio-24.0.5.tar.xz/CI/install/osx/settings.json Added
15
 
1
@@ -0,0 +1,13 @@
2
+{
3
+    "title": "OBS",
4
+    "background": "../CI/install/osx/background.tiff",
5
+    "format": "UDZO",
6
+    "compression-level": 9,
7
+    "window": { "position": { "x": 100, "y": 100 },
8
+                "size": { "width": 540, "height": 380 } },
9
+    "contents": [
10
+        { "x": 120, "y": 180, "type": "file",
11
+          "path": "./OBS.app" },
12
+        { "x": 420, "y": 180, "type": "link", "path": "/Applications" }
13
+    ]
14
+}
15
obs-studio-24.0.3.tar.xz/UI/CMakeLists.txt -> obs-studio-24.0.5.tar.xz/UI/CMakeLists.txt Changed
16
 
1
@@ -373,14 +373,6 @@
2
    target_link_libraries(obs
3
            Qt5::MacExtras)
4
    set_target_properties(obs PROPERTIES LINK_FLAGS "-pagezero_size 10000 -image_base 100000000")
5
-   set_property(
6
-       TARGET obs
7
-       APPEND
8
-       PROPERTY INSTALL_RPATH
9
-       "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/"
10
-       "/Library/Frameworks/Python.framework/Versions/3.6/lib/"
11
-       "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/"
12
-   )
13
 endif()
14
 
15
 define_graphic_modules(obs)
16
obs-studio-24.0.3.tar.xz/UI/auth-twitch.cpp -> obs-studio-24.0.5.tar.xz/UI/auth-twitch.cpp Changed
11
 
1
@@ -216,7 +216,8 @@
2
    chat->SetWidget(browser);
3
    cef->add_force_popup_url(moderation_tools_url, chat.data());
4
 
5
-   script = bttv_script;
6
+   script = "localStorage.setItem('twilight.theme', 1);";
7
+   script += bttv_script;
8
    script += ffz_script;
9
    browser->setStartupScript(script);
10
 
11
obs-studio-24.0.3.tar.xz/UI/forms/OBSBasicSettings.ui -> obs-studio-24.0.5.tar.xz/UI/forms/OBSBasicSettings.ui Changed
24
 
1
@@ -3685,19 +3685,19 @@
2
                    <item row="0" column="1">
3
                     <widget class="QComboBox" name="sampleRate">
4
                      <property name="currentText">
5
-                      <string notr="true">44.1khz</string>
6
+                      <string notr="true">44.1 kHz</string>
7
                      </property>
8
                      <property name="currentIndex">
9
                       <number>0</number>
10
                      </property>
11
                      <item>
12
                       <property name="text">
13
-                       <string>44.1khz</string>
14
+                       <string>44.1 kHz</string>
15
                       </property>
16
                      </item>
17
                      <item>
18
                       <property name="text">
19
-                       <string>48khz</string>
20
+                       <string>48 kHz</string>
21
                       </property>
22
                      </item>
23
                     </widget>
24
obs-studio-24.0.3.tar.xz/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp -> obs-studio-24.0.5.tar.xz/UI/frontend-plugins/frontend-tools/auto-scene-switcher-nix.cpp Changed
21
 
1
@@ -126,10 +126,17 @@
2
    if (status >= Success && name != nullptr) {
3
        std::string str(name);
4
        windowTitle = str;
5
+       XFree(name);
6
+   } else {
7
+       XTextProperty xtp_new_name;
8
+       if (XGetWMName(disp(), w, &xtp_new_name) != 0 &&
9
+           xtp_new_name.value != nullptr) {
10
+           std::string str((const char *)xtp_new_name.value);
11
+           windowTitle = str;
12
+           XFree(xtp_new_name.value);
13
+       }
14
    }
15
 
16
-   XFree(name);
17
-
18
    return windowTitle;
19
 }
20
 
21
obs-studio-24.0.3.tar.xz/UI/platform-osx.mm -> obs-studio-24.0.5.tar.xz/UI/platform-osx.mm Changed
34
 
1
@@ -28,11 +28,29 @@
2
 
3
 using namespace std;
4
 
5
+bool isInBundle()
6
+{
7
+   NSRunningApplication *app = [NSRunningApplication currentApplication];
8
+   return [app bundleIdentifier] != nil;
9
+}
10
+
11
 bool GetDataFilePath(const char *data, string &output)
12
 {
13
-   stringstream str;
14
-   str << OBS_DATA_PATH "/obs-studio/" << data;
15
-   output = str.str();
16
+   if (isInBundle()) {
17
+       NSRunningApplication *app =
18
+           [NSRunningApplication currentApplication];
19
+       NSURL *bundleURL = [app bundleURL];
20
+       NSString *path = [NSString
21
+           stringWithFormat:@"Contents/Resources/data/obs-studio/%@",
22
+                    [NSString stringWithUTF8String:data]];
23
+       NSURL *dataURL = [bundleURL URLByAppendingPathComponent:path];
24
+       output = [[dataURL path] UTF8String];
25
+   } else {
26
+       stringstream str;
27
+       str << OBS_DATA_PATH "/obs-studio/" << data;
28
+       output = str.str();
29
+   }
30
+
31
    return !access(output.c_str(), R_OK);
32
 }
33
 
34
obs-studio-24.0.3.tar.xz/UI/properties-view.cpp -> obs-studio-24.0.5.tar.xz/UI/properties-view.cpp Changed
10
 
1
@@ -1820,7 +1820,7 @@
2
        break;
3
    case OBS_PROPERTY_GROUP:
4
        GroupChanged(setting);
5
-       return;
6
+       break;
7
    }
8
 
9
    if (view->callback && !view->deferUpdate)
10
obs-studio-24.0.3.tar.xz/UI/window-basic-auto-config.cpp -> obs-studio-24.0.5.tar.xz/UI/window-basic-auto-config.cpp Changed
10
 
1
@@ -417,6 +417,8 @@
2
 #ifdef BROWSER_AVAILABLE
3
    std::string service = QT_TO_UTF8(ui->service->currentText());
4
 
5
+   OAuth::DeleteCookies(service);
6
+
7
    auth = OAuthStreamKey::Login(this, service);
8
    if (!!auth)
9
        OnAuthConnected();
10
obs-studio-24.0.3.tar.xz/UI/window-basic-interaction.cpp -> obs-studio-24.0.5.tar.xz/UI/window-basic-interaction.cpp Changed
12
 
1
@@ -59,6 +59,10 @@
2
    const char *name = obs_source_get_name(source);
3
    setWindowTitle(QTStr("Basic.InteractionWindow").arg(QT_UTF8(name)));
4
 
5
+   Qt::WindowFlags flags = windowFlags();
6
+   Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint;
7
+   setWindowFlags(flags & (~helpFlag));
8
+
9
    auto addDrawCallback = [this]() {
10
        obs_display_add_draw_callback(ui->preview->GetDisplay(),
11
                          OBSBasicInteraction::DrawPreview,
12
obs-studio-24.0.3.tar.xz/UI/window-basic-main-outputs.cpp -> obs-studio-24.0.5.tar.xz/UI/window-basic-main-outputs.cpp Changed
9
 
1
@@ -643,6 +643,7 @@
2
    } else if (videoEncoder == SIMPLE_ENCODER_NVENC) {
3
        UpdateRecordingSettings_nvenc(crf);
4
    }
5
+   UpdateRecordingAudioSettings();
6
 }
7
 
8
 inline void SimpleOutput::SetupOutputs()
9
obs-studio-24.0.3.tar.xz/UI/window-basic-main-scene-collections.cpp -> obs-studio-24.0.5.tar.xz/UI/window-basic-main-scene-collections.cpp Changed
92
 
1
@@ -88,6 +88,38 @@
2
    return found;
3
 }
4
 
5
+static bool GetUnusedSceneCollectionFile(std::string &name, std::string &file)
6
+{
7
+   char path[512];
8
+   size_t len;
9
+   int ret;
10
+
11
+   if (!GetFileSafeName(name.c_str(), file)) {
12
+       blog(LOG_WARNING, "Failed to create safe file name for '%s'",
13
+            name.c_str());
14
+       return false;
15
+   }
16
+
17
+   ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes/");
18
+   if (ret <= 0) {
19
+       blog(LOG_WARNING, "Failed to get scene collection config path");
20
+       return false;
21
+   }
22
+
23
+   len = file.size();
24
+   file.insert(0, path);
25
+
26
+   if (!GetClosestUnusedFileName(file, "json")) {
27
+       blog(LOG_WARNING, "Failed to get closest file name for %s",
28
+            file.c_str());
29
+       return false;
30
+   }
31
+
32
+   file.erase(file.size() - 5, 5);
33
+   file.erase(0, file.size() - len);
34
+   return true;
35
+}
36
+
37
 static bool GetSceneCollectionName(QWidget *parent, std::string &name,
38
                   std::string &file,
39
                   const char *oldName = nullptr)
40
@@ -95,9 +127,6 @@
41
    bool rename = oldName != nullptr;
42
    const char *title;
43
    const char *text;
44
-   char path[512];
45
-   size_t len;
46
-   int ret;
47
 
48
    if (rename) {
49
        title = Str("Basic.Main.RenameSceneCollection.Title");
50
@@ -128,29 +157,10 @@
51
        break;
52
    }
53
 
54
-   if (!GetFileSafeName(name.c_str(), file)) {
55
-       blog(LOG_WARNING, "Failed to create safe file name for '%s'",
56
-            name.c_str());
57
-       return false;
58
-   }
59
-
60
-   ret = GetConfigPath(path, sizeof(path), "obs-studio/basic/scenes/");
61
-   if (ret <= 0) {
62
-       blog(LOG_WARNING, "Failed to get scene collection config path");
63
-       return false;
64
-   }
65
-
66
-   len = file.size();
67
-   file.insert(0, path);
68
-
69
-   if (!GetClosestUnusedFileName(file, "json")) {
70
-       blog(LOG_WARNING, "Failed to get closest file name for %s",
71
-            file.c_str());
72
+   if (!GetUnusedSceneCollectionFile(name, file)) {
73
        return false;
74
    }
75
 
76
-   file.erase(file.size() - 5, 5);
77
-   file.erase(0, file.size() - len);
78
    return true;
79
 }
80
 
81
@@ -166,6 +176,10 @@
82
        name = QT_TO_UTF8(qname);
83
        if (SceneCollectionExists(name.c_str()))
84
            return false;
85
+
86
+       if (!GetUnusedSceneCollectionFile(name, file)) {
87
+           return false;
88
+       }
89
    }
90
 
91
    SaveProjectNow();
92
obs-studio-24.0.3.tar.xz/UI/window-basic-settings-stream.cpp -> obs-studio-24.0.5.tar.xz/UI/window-basic-settings-stream.cpp Changed
10
 
1
@@ -466,6 +466,8 @@
2
 #ifdef BROWSER_AVAILABLE
3
    std::string service = QT_TO_UTF8(ui->service->currentText());
4
 
5
+   OAuth::DeleteCookies(service);
6
+
7
    auth = OAuthStreamKey::Login(this, service);
8
    if (!!auth)
9
        OnAuthConnected();
10
obs-studio-24.0.3.tar.xz/UI/window-basic-settings.cpp -> obs-studio-24.0.5.tar.xz/UI/window-basic-settings.cpp Changed
22
 
1
@@ -2189,9 +2189,9 @@
2
 
3
    const char *str;
4
    if (sampleRate == 48000)
5
-       str = "48khz";
6
+       str = "48 kHz";
7
    else
8
-       str = "44.1khz";
9
+       str = "44.1 kHz";
10
 
11
    int sampleRateIdx = ui->sampleRate->findText(str);
12
    if (sampleRateIdx != -1)
13
@@ -3253,7 +3253,7 @@
14
    }
15
 
16
    int sampleRate = 44100;
17
-   if (sampleRateStr == "48khz")
18
+   if (sampleRateStr == "48 kHz")
19
        sampleRate = 48000;
20
 
21
    if (WidgetChanged(ui->sampleRate))
22
obs-studio-24.0.3.tar.xz/UI/window-extra-browsers.cpp -> obs-studio-24.0.5.tar.xz/UI/window-extra-browsers.cpp Changed
26
 
1
@@ -533,6 +533,24 @@
2
 
3
    dock->SetWidget(browser);
4
 
5
+   /* Add support for Twitch Dashboard panels */
6
+   if (url.contains("twitch.tv/popout") &&
7
+       url.contains("dashboard/live")) {
8
+       QRegularExpression re("twitch.tv\/popout\/([^/]+)\/");
9
+       QRegularExpressionMatch match = re.match(url);
10
+       QString username = match.captured(1);
11
+       if (username.length() > 0) {
12
+           std::string script;
13
+           script =
14
+               "Object.defineProperty(document, 'referrer', { get: () => '";
15
+           script += "https://twitch.tv/";
16
+           script += QT_TO_UTF8(username);
17
+           script += "/dashboard/live";
18
+           script += "'});";
19
+           browser->setStartupScript(script);
20
+       }
21
+   }
22
+
23
    addDockWidget(Qt::RightDockWidgetArea, dock);
24
 
25
    if (firstCreate) {
26
obs-studio-24.0.3.tar.xz/UI/window-projector.cpp -> obs-studio-24.0.5.tar.xz/UI/window-projector.cpp Changed
11
 
1
@@ -653,6 +653,9 @@
2
            source = curSource;
3
            window->source = source;
4
        }
5
+   } else if (window->type == ProjectorType::Preview &&
6
+          !main->IsPreviewProgramMode()) {
7
+       window->source = nullptr;
8
    }
9
 
10
    if (source)
11
obs-studio-24.0.3.tar.xz/cmake/Modules/ObsHelpers.cmake -> obs-studio-24.0.5.tar.xz/cmake/Modules/ObsHelpers.cmake Changed
10
 
1
@@ -61,7 +61,7 @@
2
        set(OBS_INSTALL_PREFIX "")
3
        set(OBS_RELATIVE_PREFIX "../")
4
 
5
-       set(OBS_SCRIPT_PLUGIN_DESTINATION "${OBS_DATA_DESTINATION}/obs-scripting/${_lib_suffix}bit")
6
+       set(OBS_SCRIPT_PLUGIN_DESTINATION "${OBS_DATA_DESTINATION}/obs-scripting")
7
    else()
8
        set(OBS_EXECUTABLE_DESTINATION "bin/${_lib_suffix}bit")
9
        set(OBS_EXECUTABLE32_DESTINATION "bin/32bit")
10
obs-studio-24.0.3.tar.xz/deps/media-playback/media-playback/media.c -> obs-studio-24.0.5.tar.xz/deps/media-playback/media-playback/media.c Changed
32
 
1
@@ -139,7 +139,7 @@
2
 
3
    int ret = av_read_frame(media->fmt, &pkt);
4
    if (ret < 0) {
5
-       if (ret != AVERROR_EOF)
6
+       if (ret != AVERROR_EOF && ret != AVERROR_EXIT)
7
            blog(LOG_WARNING, "MP: av_read_frame failed: %s (%d)",
8
                 av_err2str(ret), ret);
9
        return ret;
10
@@ -230,7 +230,7 @@
11
    while (!mp_media_ready_to_start(m)) {
12
        if (!m->eof) {
13
            int ret = mp_media_next_packet(m);
14
-           if (ret == AVERROR_EOF)
15
+           if (ret == AVERROR_EOF || ret == AVERROR_EXIT)
16
                m->eof = true;
17
            else if (ret < 0)
18
                return false;
19
@@ -577,8 +577,10 @@
20
        av_dict_set_int(&opts, "buffer_size", m->buffering, 0);
21
 
22
    m->fmt = avformat_alloc_context();
23
-   m->fmt->interrupt_callback.callback = interrupt_callback;
24
-   m->fmt->interrupt_callback.opaque = m;
25
+   if (!m->is_local_file) {
26
+       m->fmt->interrupt_callback.callback = interrupt_callback;
27
+       m->fmt->interrupt_callback.opaque = m;
28
+   }
29
 
30
    int ret = avformat_open_input(&m->fmt, m->path, format,
31
                      opts ? &opts : NULL);
32
obs-studio-24.0.3.tar.xz/deps/obs-scripting/CMakeLists.txt -> obs-studio-24.0.5.tar.xz/deps/obs-scripting/CMakeLists.txt Changed
13
 
1
@@ -12,6 +12,11 @@
2
        w32-pthreads)
3
 endif()
4
 
5
+if(APPLE)
6
+   set(obs-scripting_PLATFORM_DEPS
7
+       objc)
8
+endif()
9
+
10
 option(DISABLE_LUA "Disable Lua scripting support" OFF)
11
 option(DISABLE_PYTHON "Disable Python scripting support" OFF)
12
 
13
obs-studio-24.0.3.tar.xz/deps/obs-scripting/obs-scripting-lua.c -> obs-studio-24.0.5.tar.xz/deps/obs-scripting/obs-scripting-lua.c Changed
44
 
1
@@ -43,7 +43,8 @@
2
 for val in pairs(package.preload) do\n\
3
    package.preload[val] = nil\n\
4
 end\n\
5
-package.cpath = package.cpath .. \";\" .. \"%s\" .. \"/?." SO_EXT "\"\n\
6
+package.cpath = package.cpath .. \";\" .. \"%s/Contents/MacOS/?.so\" .. \";\" .. \"%s\" .. \"/?." SO_EXT
7
+                        "\"\n\
8
 require \"obslua\"\n";
9
 
10
 static const char *get_script_path_func = "\
11
@@ -1310,7 +1311,31 @@
12
    /* ---------------------------------------------- */
13
    /* Initialize Lua startup script                  */
14
 
15
-   dstr_printf(&tmp, startup_script_template, SCRIPT_DIR);
16
+   char *bundlePath = "./";
17
+
18
+#ifdef __APPLE__
19
+   Class nsRunningApplication = objc_lookUpClass("NSRunningApplication");
20
+   SEL currentAppSel = sel_getUid("currentApplication");
21
+
22
+   typedef id (*running_app_func)(Class, SEL);
23
+   running_app_func operatingSystemName = (running_app_func)objc_msgSend;
24
+   id app = operatingSystemName(nsRunningApplication, currentAppSel);
25
+
26
+   typedef id (*bundle_url_func)(id, SEL);
27
+   bundle_url_func bundleURL = (bundle_url_func)objc_msgSend;
28
+   id url = bundleURL(app, sel_getUid("bundleURL"));
29
+
30
+   typedef id (*url_path_func)(id, SEL);
31
+   url_path_func urlPath = (url_path_func)objc_msgSend;
32
+
33
+   id path = urlPath(url, sel_getUid("path"));
34
+
35
+   typedef id (*string_func)(id, SEL);
36
+   string_func utf8String = (string_func)objc_msgSend;
37
+   bundlePath = (char *)utf8String(path, sel_registerName("UTF8String"));
38
+#endif
39
+
40
+   dstr_printf(&tmp, startup_script_template, bundlePath, SCRIPT_DIR);
41
    startup_script = tmp.array;
42
 
43
    dstr_free(&dep_paths);
44
obs-studio-24.0.3.tar.xz/deps/obs-scripting/obs-scripting-python-import.c -> obs-studio-24.0.5.tar.xz/deps/obs-scripting/obs-scripting-python-import.c Changed
23
 
1
@@ -32,6 +32,12 @@
2
 #define SO_EXT ".dylib"
3
 #endif
4
 
5
+#ifdef __APPLE__
6
+#define PYTHON_LIB_SUBDIR "lib/"
7
+#else
8
+#define PYTHON_LIB_SUBDIR ""
9
+#endif
10
+
11
 bool import_python(const char *python_path)
12
 {
13
    struct dstr lib_path;
14
@@ -44,7 +50,7 @@
15
    dstr_init_copy(&lib_path, python_path);
16
    dstr_replace(&lib_path, "\\", "/");
17
    if (!dstr_is_empty(&lib_path)) {
18
-       dstr_cat(&lib_path, "/");
19
+       dstr_cat(&lib_path, "/" PYTHON_LIB_SUBDIR);
20
    }
21
    dstr_cat(&lib_path, PYTHON_LIB SO_EXT);
22
 
23
obs-studio-24.0.3.tar.xz/deps/obs-scripting/obs-scripting-python.c -> obs-studio-24.0.5.tar.xz/deps/obs-scripting/obs-scripting-python.c Changed
15
 
1
@@ -1651,6 +1651,13 @@
2
 
3
    add_to_python_path(SCRIPT_DIR);
4
 
5
+#if __APPLE__
6
+   char *exec_path = os_get_executable_path_ptr("");
7
+   if (exec_path)
8
+       add_to_python_path(exec_path);
9
+   bfree(exec_path);
10
+#endif
11
+
12
    py_obspython = PyImport_ImportModule("obspython");
13
    bool success = !py_error();
14
    if (!success) {
15
obs-studio-24.0.3.tar.xz/deps/obs-scripting/obspython/CMakeLists.txt -> obs-studio-24.0.5.tar.xz/deps/obs-scripting/obspython/CMakeLists.txt Changed
31
 
1
@@ -42,7 +42,12 @@
2
 else()
3
    SWIG_ADD_MODULE(obspython python obspython.i ../cstrcache.cpp ../cstrcache.h)
4
 endif()
5
-SWIG_LINK_LIBRARIES(obspython obs-scripting libobs ${PYTHON_LIBRARIES})
6
+
7
+IF(APPLE)
8
+   SWIG_LINK_LIBRARIES(obspython obs-scripting libobs)
9
+ELSE()
10
+   SWIG_LINK_LIBRARIES(obspython obs-scripting libobs ${PYTHON_LIBRARIES})
11
+ENDIF()
12
 
13
 function(install_plugin_bin_swig target additional_target)
14
    if(APPLE)
15
@@ -57,14 +62,7 @@
16
        PREFIX "")
17
 
18
    if (APPLE)
19
-       set_property(
20
-               TARGET ${additional_target}
21
-               APPEND
22
-               PROPERTY INSTALL_RPATH
23
-               "/usr/local/Cellar/python3/3.6.4_2/Frameworks/Python.framework/Versions/3.6/lib/"
24
-               "/Library/Frameworks/Python.framework/Versions/3.6/lib/"
25
-               "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/"
26
-       )
27
+       SET_TARGET_PROPERTIES(${additional_target} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
28
    endif()
29
 
30
    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/obspython.py"
31
obs-studio-24.0.3.tar.xz/formatcode.sh -> obs-studio-24.0.5.tar.xz/formatcode.sh Changed
17
 
1
@@ -27,5 +27,14 @@
2
     CLANG_FORMAT=clang-format
3
 fi
4
 
5
-find . -type d \( -path ./deps -o -path ./cmake -o -path ./plugins/decklink/win -o -path ./plugins/decklink/mac -o -path ./plugins/decklink/linux -o -path ./build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \
6
+find . -type d \( -path ./deps \
7
+-o -path ./cmake \
8
+-o -path ./plugins/decklink/win \
9
+-o -path ./plugins/decklink/mac \
10
+-o -path ./plugins/decklink/linux \
11
+-o -path ./plugins/enc-amf \
12
+-o -path ./plugins/mac-syphon/syphon-framework \
13
+-o -path ./plugins/obs-outputs/ftl-sdk \
14
+-o -path ./plugins/obs-vst \
15
+-o -path ./build \) -prune -type f -o -name '*.h' -or -name '*.hpp' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp' \
16
 | xargs -I{} -P ${NPROC} ${CLANG_FORMAT} -i -style=file  -fallback-style=none {}
17
obs-studio-24.0.3.tar.xz/libobs-d3d11/d3d11-rebuild.cpp -> obs-studio-24.0.5.tar.xz/libobs-d3d11/d3d11-rebuild.cpp Changed
19
 
1
@@ -178,10 +178,13 @@
2
    if (FAILED(hr))
3
        throw HRError("Failed to create vertex shader", hr);
4
 
5
-   hr = dev->CreateInputLayout(layoutData.data(), (UINT)layoutData.size(),
6
-                   data.data(), data.size(), &layout);
7
-   if (FAILED(hr))
8
-       throw HRError("Failed to create input layout", hr);
9
+   const UINT layoutSize = (UINT)layoutData.size();
10
+   if (layoutSize > 0) {
11
+       hr = dev->CreateInputLayout(layoutData.data(), layoutSize,
12
+                       data.data(), data.size(), &layout);
13
+       if (FAILED(hr))
14
+           throw HRError("Failed to create input layout", hr);
15
+   }
16
 
17
    if (constantSize) {
18
        hr = dev->CreateBuffer(&bd, NULL, &constants);
19
obs-studio-24.0.3.tar.xz/libobs-d3d11/d3d11-subsystem.cpp -> obs-studio-24.0.5.tar.xz/libobs-d3d11/d3d11-subsystem.cpp Changed
126
 
1
@@ -796,6 +796,53 @@
2
    }
3
 }
4
 
5
+static bool GetMonitorTarget(const MONITORINFOEX &info,
6
+                DISPLAYCONFIG_TARGET_DEVICE_NAME &target)
7
+{
8
+   bool found = false;
9
+
10
+   UINT32 numPath, numMode;
11
+   if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPath,
12
+                   &numMode) == ERROR_SUCCESS) {
13
+       std::vector<DISPLAYCONFIG_PATH_INFO> paths(numPath);
14
+       std::vector<DISPLAYCONFIG_MODE_INFO> modes(numMode);
15
+       if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &numPath,
16
+                      paths.data(), &numMode, modes.data(),
17
+                      nullptr) == ERROR_SUCCESS) {
18
+           paths.resize(numPath);
19
+           for (size_t i = 0; i < numPath; ++i) {
20
+               const DISPLAYCONFIG_PATH_INFO &path = paths[i];
21
+
22
+               DISPLAYCONFIG_SOURCE_DEVICE_NAME
23
+               source;
24
+               source.header.type =
25
+                   DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
26
+               source.header.size = sizeof(source);
27
+               source.header.adapterId =
28
+                   path.sourceInfo.adapterId;
29
+               source.header.id = path.sourceInfo.id;
30
+               if (DisplayConfigGetDeviceInfo(
31
+                       &source.header) == ERROR_SUCCESS &&
32
+                   wcscmp(info.szDevice,
33
+                      source.viewGdiDeviceName) == 0) {
34
+                   target.header.type =
35
+                       DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
36
+                   target.header.size = sizeof(target);
37
+                   target.header.adapterId =
38
+                       path.sourceInfo.adapterId;
39
+                   target.header.id = path.targetInfo.id;
40
+                   found = DisplayConfigGetDeviceInfo(
41
+                           &target.header) ==
42
+                       ERROR_SUCCESS;
43
+                   break;
44
+               }
45
+           }
46
+       }
47
+   }
48
+
49
+   return found;
50
+}
51
+
52
 static inline void LogAdapterMonitors(IDXGIAdapter1 *adapter)
53
 {
54
    UINT i;
55
@@ -806,15 +853,41 @@
56
        if (FAILED(output->GetDesc(&desc)))
57
            continue;
58
 
59
-       RECT rect = desc.DesktopCoordinates;
60
+       unsigned refresh = 0;
61
+
62
+       bool target_found = false;
63
+       DISPLAYCONFIG_TARGET_DEVICE_NAME target;
64
+
65
+       MONITORINFOEX info;
66
+       info.cbSize = sizeof(info);
67
+       if (GetMonitorInfo(desc.Monitor, &info)) {
68
+           target_found = GetMonitorTarget(info, target);
69
+
70
+           DEVMODE mode;
71
+           mode.dmSize = sizeof(mode);
72
+           mode.dmDriverExtra = 0;
73
+           if (EnumDisplaySettings(info.szDevice,
74
+                       ENUM_CURRENT_SETTINGS, &mode)) {
75
+               refresh = mode.dmDisplayFrequency;
76
+           }
77
+       }
78
+
79
+       if (!target_found) {
80
+           target.monitorFriendlyDeviceName[0] = 0;
81
+       }
82
+
83
+       const RECT &rect = desc.DesktopCoordinates;
84
        blog(LOG_INFO,
85
             "\t  output %u: "
86
             "pos={%d, %d}, "
87
             "size={%d, %d}, "
88
-            "attached=%s",
89
+            "attached=%s, "
90
+            "refresh=%u, "
91
+            "name=%ls",
92
             i, rect.left, rect.top, rect.right - rect.left,
93
             rect.bottom - rect.top,
94
-            desc.AttachedToDesktop ? "true" : "false");
95
+            desc.AttachedToDesktop ? "true" : "false", refresh,
96
+            target.monitorFriendlyDeviceName);
97
    }
98
 }
99
 
100
@@ -853,6 +926,25 @@
101
        blog(LOG_INFO, "\t  Shared VRAM:    %u",
102
             desc.SharedSystemMemory);
103
 
104
+       /* driver version */
105
+       LARGE_INTEGER umd;
106
+       hr = adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice),
107
+                           &umd);
108
+       if (SUCCEEDED(hr)) {
109
+           const uint64_t version = umd.QuadPart;
110
+           const uint16_t aa = (version >> 48) & 0xffff;
111
+           const uint16_t bb = (version >> 32) & 0xffff;
112
+           const uint16_t ccccc = (version >> 16) & 0xffff;
113
+           const uint16_t ddddd = version & 0xffff;
114
+           blog(LOG_INFO,
115
+                "\t  Driver Version: %" PRIu16 ".%" PRIu16
116
+                ".%" PRIu16 ".%" PRIu16,
117
+                aa, bb, ccccc, ddddd);
118
+       } else {
119
+           blog(LOG_INFO, "\t  Driver Version: Unknown (0x%X)",
120
+                (unsigned)hr);
121
+       }
122
+
123
        LogAdapterMonitors(adapter);
124
    }
125
 }
126
obs-studio-24.0.3.tar.xz/libobs/CMakeLists.txt -> obs-studio-24.0.5.tar.xz/libobs/CMakeLists.txt Changed
10
 
1
@@ -110,7 +110,7 @@
2
    endif()
3
 elseif(APPLE)
4
    set(libobs_PLATFORM_SOURCES
5
-       obs-cocoa.c
6
+       obs-cocoa.m
7
        util/threading-posix.c
8
        util/pipe-posix.c
9
        util/platform-nix.c
10
obs-studio-24.0.5.tar.xz/libobs/obs-cocoa.m Added
1778
 
1
@@ -0,0 +1,1776 @@
2
+/******************************************************************************
3
+    Copyright (C) 2013 by Ruwen Hahn <palana@stunned.de>
4
+
5
+    This program is free software: you can redistribute it and/or modify
6
+    it under the terms of the GNU General Public License as published by
7
+    the Free Software Foundation, either version 2 of the License, or
8
+    (at your option) any later version.
9
+
10
+    This program is distributed in the hope that it will be useful,
11
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+    GNU General Public License for more details.
14
+
15
+    You should have received a copy of the GNU General Public License
16
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
+******************************************************************************/
18
+
19
+#include "util/platform.h"
20
+#include "util/dstr.h"
21
+#include "obs.h"
22
+#include "obs-internal.h"
23
+
24
+#include <unistd.h>
25
+#include <sys/types.h>
26
+#include <sys/sysctl.h>
27
+
28
+#include <objc/objc.h>
29
+#include <Carbon/Carbon.h>
30
+#include <IOKit/hid/IOHIDDevice.h>
31
+#include <IOKit/hid/IOHIDManager.h>
32
+
33
+#import <AppKit/AppKit.h>
34
+
35
+bool is_in_bundle()
36
+{
37
+   NSRunningApplication *app = [NSRunningApplication currentApplication];
38
+   return [app bundleIdentifier] != nil;
39
+}
40
+
41
+const char *get_module_extension(void)
42
+{
43
+   return ".so";
44
+}
45
+
46
+static const char *module_bin[] = {
47
+   "../obs-plugins",
48
+   OBS_INSTALL_PREFIX "obs-plugins",
49
+};
50
+
51
+static const char *module_data[] = {
52
+   "../data/obs-plugins/%module%",
53
+   OBS_INSTALL_DATA_PATH "obs-plugins/%module%",
54
+};
55
+
56
+static const int module_patterns_size =
57
+   sizeof(module_bin) / sizeof(module_bin[0]);
58
+
59
+void add_default_module_paths(void)
60
+{
61
+   for (int i = 0; i < module_patterns_size; i++)
62
+       obs_add_module_path(module_bin[i], module_data[i]);
63
+
64
+   if (is_in_bundle()) {
65
+       NSRunningApplication *app =
66
+           [NSRunningApplication currentApplication];
67
+       NSURL *bundleURL = [app bundleURL];
68
+       NSURL *pluginsURL = [bundleURL
69
+           URLByAppendingPathComponent:@"Contents/Plugins"];
70
+       NSURL *dataURL = [bundleURL
71
+           URLByAppendingPathComponent:
72
+               @"Contents/Resources/data/obs-plugins/%module%"];
73
+
74
+       const char *binPath = [[pluginsURL path]
75
+           cStringUsingEncoding:NSUTF8StringEncoding];
76
+       const char *dataPath = [[dataURL path]
77
+           cStringUsingEncoding:NSUTF8StringEncoding];
78
+
79
+       obs_add_module_path(binPath, dataPath);
80
+   }
81
+}
82
+
83
+char *find_libobs_data_file(const char *file)
84
+{
85
+   struct dstr path;
86
+
87
+   if (is_in_bundle()) {
88
+       NSRunningApplication *app =
89
+           [NSRunningApplication currentApplication];
90
+       NSURL *bundleURL = [app bundleURL];
91
+       NSURL *libobsDataURL =
92
+           [bundleURL URLByAppendingPathComponent:
93
+                      @"Contents/Resources/data/libobs/"];
94
+       const char *libobsDataPath = [[libobsDataURL path]
95
+           cStringUsingEncoding:NSUTF8StringEncoding];
96
+       dstr_init_copy(&path, libobsDataPath);
97
+       dstr_cat(&path, "/");
98
+   } else {
99
+       dstr_init_copy(&path, OBS_INSTALL_DATA_PATH "/libobs/");
100
+   }
101
+
102
+   dstr_cat(&path, file);
103
+   return path.array;
104
+}
105
+
106
+static void log_processor_name(void)
107
+{
108
+   char *name = NULL;
109
+   size_t size;
110
+   int ret;
111
+
112
+   ret = sysctlbyname("machdep.cpu.brand_string", NULL, &size, NULL, 0);
113
+   if (ret != 0)
114
+       return;
115
+
116
+   name = malloc(size);
117
+
118
+   ret = sysctlbyname("machdep.cpu.brand_string", name, &size, NULL, 0);
119
+   if (ret == 0)
120
+       blog(LOG_INFO, "CPU Name: %s", name);
121
+
122
+   free(name);
123
+}
124
+
125
+static void log_processor_speed(void)
126
+{
127
+   size_t size;
128
+   long long freq;
129
+   int ret;
130
+
131
+   size = sizeof(freq);
132
+   ret = sysctlbyname("hw.cpufrequency", &freq, &size, NULL, 0);
133
+   if (ret == 0)
134
+       blog(LOG_INFO, "CPU Speed: %lldMHz", freq / 1000000);
135
+}
136
+
137
+static void log_processor_cores(void)
138
+{
139
+   blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d",
140
+        os_get_physical_cores(), os_get_logical_cores());
141
+}
142
+
143
+static void log_available_memory(void)
144
+{
145
+   size_t size;
146
+   long long memory_available;
147
+   int ret;
148
+
149
+   size = sizeof(memory_available);
150
+   ret = sysctlbyname("hw.memsize", &memory_available, &size, NULL, 0);
151
+   if (ret == 0)
152
+       blog(LOG_INFO, "Physical Memory: %lldMB Total",
153
+            memory_available / 1024 / 1024);
154
+}
155
+
156
+static void log_os_name(id pi, SEL UTF8StringSel)
157
+{
158
+   typedef int (*os_func)(id, SEL);
159
+   os_func operatingSystem = (os_func)objc_msgSend;
160
+   unsigned long os_id = (unsigned long)operatingSystem(
161
+       pi, sel_registerName("operatingSystem"));
162
+
163
+   typedef id (*os_name_func)(id, SEL);
164
+   os_name_func operatingSystemName = (os_name_func)objc_msgSend;
165
+   id os = operatingSystemName(pi,
166
+                   sel_registerName("operatingSystemName"));
167
+   typedef const char *(*utf8_func)(id, SEL);
168
+   utf8_func UTF8String = (utf8_func)objc_msgSend;
169
+   const char *name = UTF8String(os, UTF8StringSel);
170
+
171
+   if (os_id == 5 /*NSMACHOperatingSystem*/) {
172
+       blog(LOG_INFO, "OS Name: Mac OS X (%s)", name);
173
+       return;
174
+   }
175
+
176
+   blog(LOG_INFO, "OS Name: %s", name ? name : "Unknown");
177
+}
178
+
179
+static void log_os_version(id pi, SEL UTF8StringSel)
180
+{
181
+   typedef id (*version_func)(id, SEL);
182
+   version_func operatingSystemVersionString = (version_func)objc_msgSend;
183
+   id vs = operatingSystemVersionString(
184
+       pi, sel_registerName("operatingSystemVersionString"));
185
+   typedef const char *(*utf8_func)(id, SEL);
186
+   utf8_func UTF8String = (utf8_func)objc_msgSend;
187
+   const char *version = UTF8String(vs, UTF8StringSel);
188
+
189
+   blog(LOG_INFO, "OS Version: %s", version ? version : "Unknown");
190
+}
191
+
192
+static void log_os(void)
193
+{
194
+   Class NSProcessInfo = objc_getClass("NSProcessInfo");
195
+   typedef id (*func)(id, SEL);
196
+   func processInfo = (func)objc_msgSend;
197
+   id pi = processInfo((id)NSProcessInfo, sel_registerName("processInfo"));
198
+
199
+   SEL UTF8String = sel_registerName("UTF8String");
200
+
201
+   log_os_name(pi, UTF8String);
202
+   log_os_version(pi, UTF8String);
203
+}
204
+
205
+static void log_kernel_version(void)
206
+{
207
+   char kernel_version[1024];
208
+   size_t size = sizeof(kernel_version);
209
+   int ret;
210
+
211
+   ret = sysctlbyname("kern.osrelease", kernel_version, &size, NULL, 0);
212
+   if (ret == 0)
213
+       blog(LOG_INFO, "Kernel Version: %s", kernel_version);
214
+}
215
+
216
+void log_system_info(void)
217
+{
218
+   log_processor_name();
219
+   log_processor_speed();
220
+   log_processor_cores();
221
+   log_available_memory();
222
+   log_os();
223
+   log_kernel_version();
224
+}
225
+
226
+static bool dstr_from_cfstring(struct dstr *str, CFStringRef ref)
227
+{
228
+   CFIndex length = CFStringGetLength(ref);
229
+   CFIndex max_size = CFStringGetMaximumSizeForEncoding(
230
+       length, kCFStringEncodingUTF8);
231
+   dstr_reserve(str, max_size);
232
+
233
+   if (!CFStringGetCString(ref, str->array, max_size,
234
+               kCFStringEncodingUTF8))
235
+       return false;
236
+
237
+   str->len = strlen(str->array);
238
+   return true;
239
+}
240
+
241
+struct obs_hotkeys_platform {
242
+   volatile long refs;
243
+   TISInputSourceRef tis;
244
+   CFDataRef layout_data;
245
+   UCKeyboardLayout *layout;
246
+   IOHIDManagerRef manager;
247
+   DARRAY(IOHIDElementRef) keys[OBS_KEY_LAST_VALUE];
248
+};
249
+
250
+static void hotkeys_retain(struct obs_hotkeys_platform *plat)
251
+{
252
+   os_atomic_inc_long(&plat->refs);
253
+}
254
+
255
+static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat);
256
+static void hotkeys_release(struct obs_hotkeys_platform *plat)
257
+{
258
+   if (os_atomic_dec_long(&plat->refs) == -1)
259
+       free_hotkeys_platform(plat);
260
+}
261
+
262
+#define INVALID_KEY 0xff
263
+
264
+int obs_key_to_virtual_key(obs_key_t code)
265
+{
266
+   switch (code) {
267
+   case OBS_KEY_A:
268
+       return kVK_ANSI_A;
269
+   case OBS_KEY_B:
270
+       return kVK_ANSI_B;
271
+   case OBS_KEY_C:
272
+       return kVK_ANSI_C;
273
+   case OBS_KEY_D:
274
+       return kVK_ANSI_D;
275
+   case OBS_KEY_E:
276
+       return kVK_ANSI_E;
277
+   case OBS_KEY_F:
278
+       return kVK_ANSI_F;
279
+   case OBS_KEY_G:
280
+       return kVK_ANSI_G;
281
+   case OBS_KEY_H:
282
+       return kVK_ANSI_H;
283
+   case OBS_KEY_I:
284
+       return kVK_ANSI_I;
285
+   case OBS_KEY_J:
286
+       return kVK_ANSI_J;
287
+   case OBS_KEY_K:
288
+       return kVK_ANSI_K;
289
+   case OBS_KEY_L:
290
+       return kVK_ANSI_L;
291
+   case OBS_KEY_M:
292
+       return kVK_ANSI_M;
293
+   case OBS_KEY_N:
294
+       return kVK_ANSI_N;
295
+   case OBS_KEY_O:
296
+       return kVK_ANSI_O;
297
+   case OBS_KEY_P:
298
+       return kVK_ANSI_P;
299
+   case OBS_KEY_Q:
300
+       return kVK_ANSI_Q;
301
+   case OBS_KEY_R:
302
+       return kVK_ANSI_R;
303
+   case OBS_KEY_S:
304
+       return kVK_ANSI_S;
305
+   case OBS_KEY_T:
306
+       return kVK_ANSI_T;
307
+   case OBS_KEY_U:
308
+       return kVK_ANSI_U;
309
+   case OBS_KEY_V:
310
+       return kVK_ANSI_V;
311
+   case OBS_KEY_W:
312
+       return kVK_ANSI_W;
313
+   case OBS_KEY_X:
314
+       return kVK_ANSI_X;
315
+   case OBS_KEY_Y:
316
+       return kVK_ANSI_Y;
317
+   case OBS_KEY_Z:
318
+       return kVK_ANSI_Z;
319
+
320
+   case OBS_KEY_1:
321
+       return kVK_ANSI_1;
322
+   case OBS_KEY_2:
323
+       return kVK_ANSI_2;
324
+   case OBS_KEY_3:
325
+       return kVK_ANSI_3;
326
+   case OBS_KEY_4:
327
+       return kVK_ANSI_4;
328
+   case OBS_KEY_5:
329
+       return kVK_ANSI_5;
330
+   case OBS_KEY_6:
331
+       return kVK_ANSI_6;
332
+   case OBS_KEY_7:
333
+       return kVK_ANSI_7;
334
+   case OBS_KEY_8:
335
+       return kVK_ANSI_8;
336
+   case OBS_KEY_9:
337
+       return kVK_ANSI_9;
338
+   case OBS_KEY_0:
339
+       return kVK_ANSI_0;
340
+
341
+   case OBS_KEY_RETURN:
342
+       return kVK_Return;
343
+   case OBS_KEY_ESCAPE:
344
+       return kVK_Escape;
345
+   case OBS_KEY_BACKSPACE:
346
+       return kVK_Delete;
347
+   case OBS_KEY_TAB:
348
+       return kVK_Tab;
349
+   case OBS_KEY_SPACE:
350
+       return kVK_Space;
351
+   case OBS_KEY_MINUS:
352
+       return kVK_ANSI_Minus;
353
+   case OBS_KEY_EQUAL:
354
+       return kVK_ANSI_Equal;
355
+   case OBS_KEY_BRACKETLEFT:
356
+       return kVK_ANSI_LeftBracket;
357
+   case OBS_KEY_BRACKETRIGHT:
358
+       return kVK_ANSI_RightBracket;
359
+   case OBS_KEY_BACKSLASH:
360
+       return kVK_ANSI_Backslash;
361
+   case OBS_KEY_SEMICOLON:
362
+       return kVK_ANSI_Semicolon;
363
+   case OBS_KEY_QUOTE:
364
+       return kVK_ANSI_Quote;
365
+   case OBS_KEY_DEAD_GRAVE:
366
+       return kVK_ANSI_Grave;
367
+   case OBS_KEY_COMMA:
368
+       return kVK_ANSI_Comma;
369
+   case OBS_KEY_PERIOD:
370
+       return kVK_ANSI_Period;
371
+   case OBS_KEY_SLASH:
372
+       return kVK_ANSI_Slash;
373
+   case OBS_KEY_CAPSLOCK:
374
+       return kVK_CapsLock;
375
+   case OBS_KEY_SECTION:
376
+       return kVK_ISO_Section;
377
+
378
+   case OBS_KEY_F1:
379
+       return kVK_F1;
380
+   case OBS_KEY_F2:
381
+       return kVK_F2;
382
+   case OBS_KEY_F3:
383
+       return kVK_F3;
384
+   case OBS_KEY_F4:
385
+       return kVK_F4;
386
+   case OBS_KEY_F5:
387
+       return kVK_F5;
388
+   case OBS_KEY_F6:
389
+       return kVK_F6;
390
+   case OBS_KEY_F7:
391
+       return kVK_F7;
392
+   case OBS_KEY_F8:
393
+       return kVK_F8;
394
+   case OBS_KEY_F9:
395
+       return kVK_F9;
396
+   case OBS_KEY_F10:
397
+       return kVK_F10;
398
+   case OBS_KEY_F11:
399
+       return kVK_F11;
400
+   case OBS_KEY_F12:
401
+       return kVK_F12;
402
+
403
+   case OBS_KEY_HELP:
404
+       return kVK_Help;
405
+   case OBS_KEY_HOME:
406
+       return kVK_Home;
407
+   case OBS_KEY_PAGEUP:
408
+       return kVK_PageUp;
409
+   case OBS_KEY_DELETE:
410
+       return kVK_ForwardDelete;
411
+   case OBS_KEY_END:
412
+       return kVK_End;
413
+   case OBS_KEY_PAGEDOWN:
414
+       return kVK_PageDown;
415
+
416
+   case OBS_KEY_RIGHT:
417
+       return kVK_RightArrow;
418
+   case OBS_KEY_LEFT:
419
+       return kVK_LeftArrow;
420
+   case OBS_KEY_DOWN:
421
+       return kVK_DownArrow;
422
+   case OBS_KEY_UP:
423
+       return kVK_UpArrow;
424
+
425
+   case OBS_KEY_CLEAR:
426
+       return kVK_ANSI_KeypadClear;
427
+   case OBS_KEY_NUMSLASH:
428
+       return kVK_ANSI_KeypadDivide;
429
+   case OBS_KEY_NUMASTERISK:
430
+       return kVK_ANSI_KeypadMultiply;
431
+   case OBS_KEY_NUMMINUS:
432
+       return kVK_ANSI_KeypadMinus;
433
+   case OBS_KEY_NUMPLUS:
434
+       return kVK_ANSI_KeypadPlus;
435
+   case OBS_KEY_ENTER:
436
+       return kVK_ANSI_KeypadEnter;
437
+
438
+   case OBS_KEY_NUM1:
439
+       return kVK_ANSI_Keypad1;
440
+   case OBS_KEY_NUM2:
441
+       return kVK_ANSI_Keypad2;
442
+   case OBS_KEY_NUM3:
443
+       return kVK_ANSI_Keypad3;
444
+   case OBS_KEY_NUM4:
445
+       return kVK_ANSI_Keypad4;
446
+   case OBS_KEY_NUM5:
447
+       return kVK_ANSI_Keypad5;
448
+   case OBS_KEY_NUM6:
449
+       return kVK_ANSI_Keypad6;
450
+   case OBS_KEY_NUM7:
451
+       return kVK_ANSI_Keypad7;
452
+   case OBS_KEY_NUM8:
453
+       return kVK_ANSI_Keypad8;
454
+   case OBS_KEY_NUM9:
455
+       return kVK_ANSI_Keypad9;
456
+   case OBS_KEY_NUM0:
457
+       return kVK_ANSI_Keypad0;
458
+
459
+   case OBS_KEY_NUMPERIOD:
460
+       return kVK_ANSI_KeypadDecimal;
461
+   case OBS_KEY_NUMEQUAL:
462
+       return kVK_ANSI_KeypadEquals;
463
+
464
+   case OBS_KEY_F13:
465
+       return kVK_F13;
466
+   case OBS_KEY_F14:
467
+       return kVK_F14;
468
+   case OBS_KEY_F15:
469
+       return kVK_F15;
470
+   case OBS_KEY_F16:
471
+       return kVK_F16;
472
+   case OBS_KEY_F17:
473
+       return kVK_F17;
474
+   case OBS_KEY_F18:
475
+       return kVK_F18;
476
+   case OBS_KEY_F19:
477
+       return kVK_F19;
478
+   case OBS_KEY_F20:
479
+       return kVK_F20;
480
+
481
+   case OBS_KEY_CONTROL:
482
+       return kVK_Control;
483
+   case OBS_KEY_SHIFT:
484
+       return kVK_Shift;
485
+   case OBS_KEY_ALT:
486
+       return kVK_Option;
487
+   case OBS_KEY_META:
488
+       return kVK_Command;
489
+       //case OBS_KEY_CONTROL: return kVK_RightControl;
490
+       //case OBS_KEY_SHIFT: return kVK_RightShift;
491
+       //case OBS_KEY_ALT: return kVK_RightOption;
492
+       //case OBS_KEY_META: return 0x36;
493
+
494
+   case OBS_KEY_NONE:
495
+   case OBS_KEY_LAST_VALUE:
496
+   default:
497
+       break;
498
+   }
499
+   return INVALID_KEY;
500
+}
501
+
502
+static bool localized_key_to_str(obs_key_t key, struct dstr *str)
503
+{
504
+#define MAP_KEY(k, s)                                             \
505
+   case k:                                                   \
506
+       dstr_copy(str, obs_get_hotkey_translation(k, s)); \
507
+       return true
508
+
509
+#define MAP_BUTTON(i)                                                         \
510
+   case OBS_KEY_MOUSE##i:                                                \
511
+       dstr_copy(str, obs_get_hotkey_translation(key, "Mouse " #i)); \
512
+       return true
513
+
514
+   switch (key) {
515
+       MAP_KEY(OBS_KEY_SPACE, "Space");
516
+       MAP_KEY(OBS_KEY_NUMEQUAL, "= (Keypad)");
517
+       MAP_KEY(OBS_KEY_NUMASTERISK, "* (Keypad)");
518
+       MAP_KEY(OBS_KEY_NUMPLUS, "+ (Keypad)");
519
+       MAP_KEY(OBS_KEY_NUMMINUS, "- (Keypad)");
520
+       MAP_KEY(OBS_KEY_NUMPERIOD, ". (Keypad)");
521
+       MAP_KEY(OBS_KEY_NUMSLASH, "/ (Keypad)");
522
+       MAP_KEY(OBS_KEY_NUM0, "0 (Keypad)");
523
+       MAP_KEY(OBS_KEY_NUM1, "1 (Keypad)");
524
+       MAP_KEY(OBS_KEY_NUM2, "2 (Keypad)");
525
+       MAP_KEY(OBS_KEY_NUM3, "3 (Keypad)");
526
+       MAP_KEY(OBS_KEY_NUM4, "4 (Keypad)");
527
+       MAP_KEY(OBS_KEY_NUM5, "5 (Keypad)");
528
+       MAP_KEY(OBS_KEY_NUM6, "6 (Keypad)");
529
+       MAP_KEY(OBS_KEY_NUM7, "7 (Keypad)");
530
+       MAP_KEY(OBS_KEY_NUM8, "8 (Keypad)");
531
+       MAP_KEY(OBS_KEY_NUM9, "9 (Keypad)");
532
+
533
+       MAP_BUTTON(1);
534
+       MAP_BUTTON(2);
535
+       MAP_BUTTON(3);
536
+       MAP_BUTTON(4);
537
+       MAP_BUTTON(5);
538
+       MAP_BUTTON(6);
539
+       MAP_BUTTON(7);
540
+       MAP_BUTTON(8);
541
+       MAP_BUTTON(9);
542
+       MAP_BUTTON(10);
543
+       MAP_BUTTON(11);
544
+       MAP_BUTTON(12);
545
+       MAP_BUTTON(13);
546
+       MAP_BUTTON(14);
547
+       MAP_BUTTON(15);
548
+       MAP_BUTTON(16);
549
+       MAP_BUTTON(17);
550
+       MAP_BUTTON(18);
551
+       MAP_BUTTON(19);
552
+       MAP_BUTTON(20);
553
+       MAP_BUTTON(21);
554
+       MAP_BUTTON(22);
555
+       MAP_BUTTON(23);
556
+       MAP_BUTTON(24);
557
+       MAP_BUTTON(25);
558
+       MAP_BUTTON(26);
559
+       MAP_BUTTON(27);
560
+       MAP_BUTTON(28);
561
+       MAP_BUTTON(29);
562
+   default:
563
+       break;
564
+   }
565
+#undef MAP_BUTTON
566
+#undef MAP_KEY
567
+
568
+   return false;
569
+}
570
+
571
+static bool code_to_str(int code, struct dstr *str)
572
+{
573
+#define MAP_GLYPH(c, g)                                \
574
+   case c:                                        \
575
+       dstr_from_wcs(str, (wchar_t[]){g, 0}); \
576
+       return true
577
+#define MAP_STR(c, s)              \
578
+   case c:                    \
579
+       dstr_copy(str, s); \
580
+       return true
581
+   switch (code) {
582
+       MAP_GLYPH(kVK_Return, 0x21A9);
583
+       MAP_GLYPH(kVK_Escape, 0x238B);
584
+       MAP_GLYPH(kVK_Delete, 0x232B);
585
+       MAP_GLYPH(kVK_Tab, 0x21e5);
586
+       MAP_GLYPH(kVK_CapsLock, 0x21EA);
587
+       MAP_GLYPH(kVK_ANSI_KeypadClear, 0x2327);
588
+       MAP_GLYPH(kVK_ANSI_KeypadEnter, 0x2305);
589
+       MAP_GLYPH(kVK_Help, 0x003F);
590
+       MAP_GLYPH(kVK_Home, 0x2196);
591
+       MAP_GLYPH(kVK_PageUp, 0x21de);
592
+       MAP_GLYPH(kVK_ForwardDelete, 0x2326);
593
+       MAP_GLYPH(kVK_End, 0x2198);
594
+       MAP_GLYPH(kVK_PageDown, 0x21df);
595
+
596
+       MAP_GLYPH(kVK_RightArrow, 0x2192);
597
+       MAP_GLYPH(kVK_LeftArrow, 0x2190);
598
+       MAP_GLYPH(kVK_DownArrow, 0x2193);
599
+       MAP_GLYPH(kVK_UpArrow, 0x2191);
600
+
601
+       MAP_STR(kVK_F1, "F1");
602
+       MAP_STR(kVK_F2, "F2");
603
+       MAP_STR(kVK_F3, "F3");
604
+       MAP_STR(kVK_F4, "F4");
605
+       MAP_STR(kVK_F5, "F5");
606
+       MAP_STR(kVK_F6, "F6");
607
+       MAP_STR(kVK_F7, "F7");
608
+       MAP_STR(kVK_F8, "F8");
609
+       MAP_STR(kVK_F9, "F9");
610
+       MAP_STR(kVK_F10, "F10");
611
+       MAP_STR(kVK_F11, "F11");
612
+       MAP_STR(kVK_F12, "F12");
613
+       MAP_STR(kVK_F13, "F13");
614
+       MAP_STR(kVK_F14, "F14");
615
+       MAP_STR(kVK_F15, "F15");
616
+       MAP_STR(kVK_F16, "F16");
617
+       MAP_STR(kVK_F17, "F17");
618
+       MAP_STR(kVK_F18, "F18");
619
+       MAP_STR(kVK_F19, "F19");
620
+       MAP_STR(kVK_F20, "F20");
621
+       MAP_GLYPH(kVK_Control, kControlUnicode);
622
+       MAP_GLYPH(kVK_Shift, kShiftUnicode);
623
+       MAP_GLYPH(kVK_Option, kOptionUnicode);
624
+       MAP_GLYPH(kVK_Command, kCommandUnicode);
625
+       MAP_GLYPH(kVK_RightControl, kControlUnicode);
626
+       MAP_GLYPH(kVK_RightShift, kShiftUnicode);
627
+       MAP_GLYPH(kVK_RightOption, kOptionUnicode);
628
+   }
629
+#undef MAP_STR
630
+#undef MAP_GLYPH
631
+
632
+   return false;
633
+}
634
+
635
+void obs_key_to_str(obs_key_t key, struct dstr *str)
636
+{
637
+   if (localized_key_to_str(key, str))
638
+       return;
639
+
640
+   int code = obs_key_to_virtual_key(key);
641
+   if (code_to_str(code, str))
642
+       return;
643
+
644
+   if (code == INVALID_KEY) {
645
+       blog(LOG_ERROR,
646
+            "hotkey-cocoa: Got invalid key while "
647
+            "translating key '%d' (%s)",
648
+            key, obs_key_to_name(key));
649
+       goto err;
650
+   }
651
+
652
+   struct obs_hotkeys_platform *plat = NULL;
653
+
654
+   if (obs) {
655
+       pthread_mutex_lock(&obs->hotkeys.mutex);
656
+       plat = obs->hotkeys.platform_context;
657
+       hotkeys_retain(plat);
658
+       pthread_mutex_unlock(&obs->hotkeys.mutex);
659
+   }
660
+
661
+   if (!plat) {
662
+       blog(LOG_ERROR,
663
+            "hotkey-cocoa: Could not get hotkey platform "
664
+            "while translating key '%d' (%s)",
665
+            key, obs_key_to_name(key));
666
+       goto err;
667
+   }
668
+
669
+   const UniCharCount max_length = 16;
670
+   UInt32 dead_key_state = 0;
671
+   UniChar buffer[max_length];
672
+   UniCharCount len = 0;
673
+
674
+   OSStatus err =
675
+       UCKeyTranslate(plat->layout, code, kUCKeyActionDown,
676
+                  0x104, //caps lock for upper case letters
677
+                  LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit,
678
+                  &dead_key_state, max_length, &len, buffer);
679
+
680
+   if (err == noErr && len <= 0 && dead_key_state) {
681
+       err = UCKeyTranslate(plat->layout, kVK_Space, kUCKeyActionDown,
682
+                    0x104, LMGetKbdType(),
683
+                    kUCKeyTranslateNoDeadKeysBit,
684
+                    &dead_key_state, max_length, &len, buffer);
685
+   }
686
+
687
+   hotkeys_release(plat);
688
+
689
+   if (err != noErr) {
690
+       blog(LOG_ERROR,
691
+            "hotkey-cocoa: Error while translating key '%d'"
692
+            " (0x%x, %s) to string: %d",
693
+            key, code, obs_key_to_name(key), err);
694
+       goto err;
695
+   }
696
+
697
+   if (len == 0) {
698
+       blog(LOG_ERROR,
699
+            "hotkey-cocoa: Got 0 length string while "
700
+            "translating '%d' (0x%x, %s) to string",
701
+            key, code, obs_key_to_name(key));
702
+       goto err;
703
+   }
704
+
705
+   CFStringRef string = CFStringCreateWithCharactersNoCopy(
706
+       NULL, buffer, len, kCFAllocatorNull);
707
+   if (!string) {
708
+       blog(LOG_ERROR,
709
+            "hotkey-cocoa: Could not create CFStringRef "
710
+            "while translating '%d' (0x%x, %s) to string",
711
+            key, code, obs_key_to_name(key));
712
+       goto err;
713
+   }
714
+
715
+   if (!dstr_from_cfstring(str, string)) {
716
+       blog(LOG_ERROR,
717
+            "hotkey-cocoa: Could not translate CFStringRef "
718
+            "to CString while translating '%d' (0x%x, %s)",
719
+            key, code, obs_key_to_name(key));
720
+
721
+       goto release;
722
+   }
723
+
724
+   CFRelease(string);
725
+   return;
726
+
727
+release:
728
+   CFRelease(string);
729
+err:
730
+   dstr_copy(str, obs_key_to_name(key));
731
+}
732
+
733
+#define OBS_COCOA_MODIFIER_SIZE 7
734
+static void unichar_to_utf8(const UniChar *c, char *buff)
735
+{
736
+   CFStringRef string = CFStringCreateWithCharactersNoCopy(
737
+       NULL, c, 2, kCFAllocatorNull);
738
+   if (!string) {
739
+       blog(LOG_ERROR, "hotkey-cocoa: Could not create CFStringRef "
740
+               "while populating modifier strings");
741
+       return;
742
+   }
743
+
744
+   if (!CFStringGetCString(string, buff, OBS_COCOA_MODIFIER_SIZE,
745
+               kCFStringEncodingUTF8))
746
+       blog(LOG_ERROR,
747
+            "hotkey-cocoa: Error while populating "
748
+            " modifier string with glyph %d (0x%x)",
749
+            c[0], c[0]);
750
+
751
+   CFRelease(string);
752
+}
753
+
754
+static char ctrl_str[OBS_COCOA_MODIFIER_SIZE];
755
+static char opt_str[OBS_COCOA_MODIFIER_SIZE];
756
+static char shift_str[OBS_COCOA_MODIFIER_SIZE];
757
+static char cmd_str[OBS_COCOA_MODIFIER_SIZE];
758
+static void init_utf_8_strings(void)
759
+{
760
+   const UniChar ctrl_uni[] = {kControlUnicode, 0};
761
+   const UniChar opt_uni[] = {kOptionUnicode, 0};
762
+   const UniChar shift_uni[] = {kShiftUnicode, 0};
763
+   const UniChar cmd_uni[] = {kCommandUnicode, 0};
764
+
765
+   unichar_to_utf8(ctrl_uni, ctrl_str);
766
+   unichar_to_utf8(opt_uni, opt_str);
767
+   unichar_to_utf8(shift_uni, shift_str);
768
+   unichar_to_utf8(cmd_uni, cmd_str);
769
+}
770
+
771
+static pthread_once_t strings_token = PTHREAD_ONCE_INIT;
772
+void obs_key_combination_to_str(obs_key_combination_t key, struct dstr *str)
773
+{
774
+   struct dstr key_str = {0};
775
+   if (key.key != OBS_KEY_NONE)
776
+       obs_key_to_str(key.key, &key_str);
777
+
778
+   int res = pthread_once(&strings_token, init_utf_8_strings);
779
+   if (res) {
780
+       blog(LOG_ERROR,
781
+            "hotkeys-cocoa: Error while translating "
782
+            "modifiers %d (0x%x)",
783
+            res, res);
784
+       dstr_move(str, &key_str);
785
+       return;
786
+   }
787
+
788
+#define CHECK_MODIFIER(mod, str) ((key.modifiers & mod) ? str : "")
789
+   dstr_printf(str, "%s%s%s%s%s",
790
+           CHECK_MODIFIER(INTERACT_CONTROL_KEY, ctrl_str),
791
+           CHECK_MODIFIER(INTERACT_ALT_KEY, opt_str),
792
+           CHECK_MODIFIER(INTERACT_SHIFT_KEY, shift_str),
793
+           CHECK_MODIFIER(INTERACT_COMMAND_KEY, cmd_str),
794
+           key_str.len ? key_str.array : "");
795
+#undef CHECK_MODIFIER
796
+
797
+   dstr_free(&key_str);
798
+}
799
+
800
+static inline CFDictionaryRef copy_device_mask(UInt32 page, UInt32 usage)
801
+{
802
+   CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
803
+       kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks,
804
+       &kCFTypeDictionaryValueCallBacks);
805
+
806
+   CFNumberRef value;
807
+   // Add the page value.
808
+   value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
809
+   CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), value);
810
+   CFRelease(value);
811
+
812
+   // Add the usage value (which is only valid if page value exists).
813
+   value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
814
+   CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), value);
815
+   CFRelease(value);
816
+
817
+   return dict;
818
+}
819
+
820
+static CFSetRef copy_devices(obs_hotkeys_platform_t *plat, UInt32 page,
821
+                UInt32 usage)
822
+{
823
+   CFDictionaryRef mask = copy_device_mask(page, usage);
824
+   IOHIDManagerSetDeviceMatching(plat->manager, mask);
825
+   CFRelease(mask);
826
+
827
+   CFSetRef devices = IOHIDManagerCopyDevices(plat->manager);
828
+   if (!devices)
829
+       return NULL;
830
+
831
+   if (CFSetGetCount(devices) < 1) {
832
+       CFRelease(devices);
833
+       return NULL;
834
+   }
835
+
836
+   return devices;
837
+}
838
+
839
+static UInt16 usage_to_carbon(UInt32 usage)
840
+{
841
+   switch (usage) {
842
+   case kHIDUsage_KeyboardErrorRollOver:
843
+       return INVALID_KEY;
844
+   case kHIDUsage_KeyboardPOSTFail:
845
+       return INVALID_KEY;
846
+   case kHIDUsage_KeyboardErrorUndefined:
847
+       return INVALID_KEY;
848
+
849
+   case kHIDUsage_KeyboardA:
850
+       return kVK_ANSI_A;
851
+   case kHIDUsage_KeyboardB:
852
+       return kVK_ANSI_B;
853
+   case kHIDUsage_KeyboardC:
854
+       return kVK_ANSI_C;
855
+   case kHIDUsage_KeyboardD:
856
+       return kVK_ANSI_D;
857
+   case kHIDUsage_KeyboardE:
858
+       return kVK_ANSI_E;
859
+   case kHIDUsage_KeyboardF:
860
+       return kVK_ANSI_F;
861
+   case kHIDUsage_KeyboardG:
862
+       return kVK_ANSI_G;
863
+   case kHIDUsage_KeyboardH:
864
+       return kVK_ANSI_H;
865
+   case kHIDUsage_KeyboardI:
866
+       return kVK_ANSI_I;
867
+   case kHIDUsage_KeyboardJ:
868
+       return kVK_ANSI_J;
869
+   case kHIDUsage_KeyboardK:
870
+       return kVK_ANSI_K;
871
+   case kHIDUsage_KeyboardL:
872
+       return kVK_ANSI_L;
873
+   case kHIDUsage_KeyboardM:
874
+       return kVK_ANSI_M;
875
+   case kHIDUsage_KeyboardN:
876
+       return kVK_ANSI_N;
877
+   case kHIDUsage_KeyboardO:
878
+       return kVK_ANSI_O;
879
+   case kHIDUsage_KeyboardP:
880
+       return kVK_ANSI_P;
881
+   case kHIDUsage_KeyboardQ:
882
+       return kVK_ANSI_Q;
883
+   case kHIDUsage_KeyboardR:
884
+       return kVK_ANSI_R;
885
+   case kHIDUsage_KeyboardS:
886
+       return kVK_ANSI_S;
887
+   case kHIDUsage_KeyboardT:
888
+       return kVK_ANSI_T;
889
+   case kHIDUsage_KeyboardU:
890
+       return kVK_ANSI_U;
891
+   case kHIDUsage_KeyboardV:
892
+       return kVK_ANSI_V;
893
+   case kHIDUsage_KeyboardW:
894
+       return kVK_ANSI_W;
895
+   case kHIDUsage_KeyboardX:
896
+       return kVK_ANSI_X;
897
+   case kHIDUsage_KeyboardY:
898
+       return kVK_ANSI_Y;
899
+   case kHIDUsage_KeyboardZ:
900
+       return kVK_ANSI_Z;
901
+
902
+   case kHIDUsage_Keyboard1:
903
+       return kVK_ANSI_1;
904
+   case kHIDUsage_Keyboard2:
905
+       return kVK_ANSI_2;
906
+   case kHIDUsage_Keyboard3:
907
+       return kVK_ANSI_3;
908
+   case kHIDUsage_Keyboard4:
909
+       return kVK_ANSI_4;
910
+   case kHIDUsage_Keyboard5:
911
+       return kVK_ANSI_5;
912
+   case kHIDUsage_Keyboard6:
913
+       return kVK_ANSI_6;
914
+   case kHIDUsage_Keyboard7:
915
+       return kVK_ANSI_7;
916
+   case kHIDUsage_Keyboard8:
917
+       return kVK_ANSI_8;
918
+   case kHIDUsage_Keyboard9:
919
+       return kVK_ANSI_9;
920
+   case kHIDUsage_Keyboard0:
921
+       return kVK_ANSI_0;
922
+
923
+   case kHIDUsage_KeyboardReturnOrEnter:
924
+       return kVK_Return;
925
+   case kHIDUsage_KeyboardEscape:
926
+       return kVK_Escape;
927
+   case kHIDUsage_KeyboardDeleteOrBackspace:
928
+       return kVK_Delete;
929
+   case kHIDUsage_KeyboardTab:
930
+       return kVK_Tab;
931
+   case kHIDUsage_KeyboardSpacebar:
932
+       return kVK_Space;
933
+   case kHIDUsage_KeyboardHyphen:
934
+       return kVK_ANSI_Minus;
935
+   case kHIDUsage_KeyboardEqualSign:
936
+       return kVK_ANSI_Equal;
937
+   case kHIDUsage_KeyboardOpenBracket:
938
+       return kVK_ANSI_LeftBracket;
939
+   case kHIDUsage_KeyboardCloseBracket:
940
+       return kVK_ANSI_RightBracket;
941
+   case kHIDUsage_KeyboardBackslash:
942
+       return kVK_ANSI_Backslash;
943
+   case kHIDUsage_KeyboardNonUSPound:
944
+       return INVALID_KEY;
945
+   case kHIDUsage_KeyboardSemicolon:
946
+       return kVK_ANSI_Semicolon;
947
+   case kHIDUsage_KeyboardQuote:
948
+       return kVK_ANSI_Quote;
949
+   case kHIDUsage_KeyboardGraveAccentAndTilde:
950
+       return kVK_ANSI_Grave;
951
+   case kHIDUsage_KeyboardComma:
952
+       return kVK_ANSI_Comma;
953
+   case kHIDUsage_KeyboardPeriod:
954
+       return kVK_ANSI_Period;
955
+   case kHIDUsage_KeyboardSlash:
956
+       return kVK_ANSI_Slash;
957
+   case kHIDUsage_KeyboardCapsLock:
958
+       return kVK_CapsLock;
959
+
960
+   case kHIDUsage_KeyboardF1:
961
+       return kVK_F1;
962
+   case kHIDUsage_KeyboardF2:
963
+       return kVK_F2;
964
+   case kHIDUsage_KeyboardF3:
965
+       return kVK_F3;
966
+   case kHIDUsage_KeyboardF4:
967
+       return kVK_F4;
968
+   case kHIDUsage_KeyboardF5:
969
+       return kVK_F5;
970
+   case kHIDUsage_KeyboardF6:
971
+       return kVK_F6;
972
+   case kHIDUsage_KeyboardF7:
973
+       return kVK_F7;
974
+   case kHIDUsage_KeyboardF8:
975
+       return kVK_F8;
976
+   case kHIDUsage_KeyboardF9:
977
+       return kVK_F9;
978
+   case kHIDUsage_KeyboardF10:
979
+       return kVK_F10;
980
+   case kHIDUsage_KeyboardF11:
981
+       return kVK_F11;
982
+   case kHIDUsage_KeyboardF12:
983
+       return kVK_F12;
984
+
985
+   case kHIDUsage_KeyboardPrintScreen:
986
+       return INVALID_KEY;
987
+   case kHIDUsage_KeyboardScrollLock:
988
+       return INVALID_KEY;
989
+   case kHIDUsage_KeyboardPause:
990
+       return INVALID_KEY;
991
+   case kHIDUsage_KeyboardInsert:
992
+       return kVK_Help;
993
+   case kHIDUsage_KeyboardHome:
994
+       return kVK_Home;
995
+   case kHIDUsage_KeyboardPageUp:
996
+       return kVK_PageUp;
997
+   case kHIDUsage_KeyboardDeleteForward:
998
+       return kVK_ForwardDelete;
999
+   case kHIDUsage_KeyboardEnd:
1000
+       return kVK_End;
1001
+   case kHIDUsage_KeyboardPageDown:
1002
+       return kVK_PageDown;
1003
+
1004
+   case kHIDUsage_KeyboardRightArrow:
1005
+       return kVK_RightArrow;
1006
+   case kHIDUsage_KeyboardLeftArrow:
1007
+       return kVK_LeftArrow;
1008
+   case kHIDUsage_KeyboardDownArrow:
1009
+       return kVK_DownArrow;
1010
+   case kHIDUsage_KeyboardUpArrow:
1011
+       return kVK_UpArrow;
1012
+
1013
+   case kHIDUsage_KeypadNumLock:
1014
+       return kVK_ANSI_KeypadClear;
1015
+   case kHIDUsage_KeypadSlash:
1016
+       return kVK_ANSI_KeypadDivide;
1017
+   case kHIDUsage_KeypadAsterisk:
1018
+       return kVK_ANSI_KeypadMultiply;
1019
+   case kHIDUsage_KeypadHyphen:
1020
+       return kVK_ANSI_KeypadMinus;
1021
+   case kHIDUsage_KeypadPlus:
1022
+       return kVK_ANSI_KeypadPlus;
1023
+   case kHIDUsage_KeypadEnter:
1024
+       return kVK_ANSI_KeypadEnter;
1025
+
1026
+   case kHIDUsage_Keypad1:
1027
+       return kVK_ANSI_Keypad1;
1028
+   case kHIDUsage_Keypad2:
1029
+       return kVK_ANSI_Keypad2;
1030
+   case kHIDUsage_Keypad3:
1031
+       return kVK_ANSI_Keypad3;
1032
+   case kHIDUsage_Keypad4:
1033
+       return kVK_ANSI_Keypad4;
1034
+   case kHIDUsage_Keypad5:
1035
+       return kVK_ANSI_Keypad5;
1036
+   case kHIDUsage_Keypad6:
1037
+       return kVK_ANSI_Keypad6;
1038
+   case kHIDUsage_Keypad7:
1039
+       return kVK_ANSI_Keypad7;
1040
+   case kHIDUsage_Keypad8:
1041
+       return kVK_ANSI_Keypad8;
1042
+   case kHIDUsage_Keypad9:
1043
+       return kVK_ANSI_Keypad9;
1044
+   case kHIDUsage_Keypad0:
1045
+       return kVK_ANSI_Keypad0;
1046
+
1047
+   case kHIDUsage_KeypadPeriod:
1048
+       return kVK_ANSI_KeypadDecimal;
1049
+   case kHIDUsage_KeyboardNonUSBackslash:
1050
+       return INVALID_KEY;
1051
+   case kHIDUsage_KeyboardApplication:
1052
+       return kVK_F13;
1053
+   case kHIDUsage_KeyboardPower:
1054
+       return INVALID_KEY;
1055
+   case kHIDUsage_KeypadEqualSign:
1056
+       return kVK_ANSI_KeypadEquals;
1057
+
1058
+   case kHIDUsage_KeyboardF13:
1059
+       return kVK_F13;
1060
+   case kHIDUsage_KeyboardF14:
1061
+       return kVK_F14;
1062
+   case kHIDUsage_KeyboardF15:
1063
+       return kVK_F15;
1064
+   case kHIDUsage_KeyboardF16:
1065
+       return kVK_F16;
1066
+   case kHIDUsage_KeyboardF17:
1067
+       return kVK_F17;
1068
+   case kHIDUsage_KeyboardF18:
1069
+       return kVK_F18;
1070
+   case kHIDUsage_KeyboardF19:
1071
+       return kVK_F19;
1072
+   case kHIDUsage_KeyboardF20:
1073
+       return kVK_F20;
1074
+   case kHIDUsage_KeyboardF21:
1075
+       return INVALID_KEY;
1076
+   case kHIDUsage_KeyboardF22:
1077
+       return INVALID_KEY;
1078
+   case kHIDUsage_KeyboardF23:
1079
+       return INVALID_KEY;
1080
+   case kHIDUsage_KeyboardF24:
1081
+       return INVALID_KEY;
1082
+
1083
+   case kHIDUsage_KeyboardExecute:
1084
+       return INVALID_KEY;
1085
+   case kHIDUsage_KeyboardHelp:
1086
+       return INVALID_KEY;
1087
+   case kHIDUsage_KeyboardMenu:
1088
+       return 0x7F;
1089
+   case kHIDUsage_KeyboardSelect:
1090
+       return kVK_ANSI_KeypadEnter;
1091
+   case kHIDUsage_KeyboardStop:
1092
+       return INVALID_KEY;
1093
+   case kHIDUsage_KeyboardAgain:
1094
+       return INVALID_KEY;
1095
+   case kHIDUsage_KeyboardUndo:
1096
+       return INVALID_KEY;
1097
+   case kHIDUsage_KeyboardCut:
1098
+       return INVALID_KEY;
1099
+   case kHIDUsage_KeyboardCopy:
1100
+       return INVALID_KEY;
1101
+   case kHIDUsage_KeyboardPaste:
1102
+       return INVALID_KEY;
1103
+   case kHIDUsage_KeyboardFind:
1104
+       return INVALID_KEY;
1105
+
1106
+   case kHIDUsage_KeyboardMute:
1107
+       return kVK_Mute;
1108
+   case kHIDUsage_KeyboardVolumeUp:
1109
+       return kVK_VolumeUp;
1110
+   case kHIDUsage_KeyboardVolumeDown:
1111
+       return kVK_VolumeDown;
1112
+
1113
+   case kHIDUsage_KeyboardLockingCapsLock:
1114
+       return INVALID_KEY;
1115
+   case kHIDUsage_KeyboardLockingNumLock:
1116
+       return INVALID_KEY;
1117
+   case kHIDUsage_KeyboardLockingScrollLock:
1118
+       return INVALID_KEY;
1119
+
1120
+   case kHIDUsage_KeypadComma:
1121
+       return INVALID_KEY;
1122
+   case kHIDUsage_KeypadEqualSignAS400:
1123
+       return INVALID_KEY;
1124
+   case kHIDUsage_KeyboardInternational1:
1125
+       return INVALID_KEY;
1126
+   case kHIDUsage_KeyboardInternational2:
1127
+       return INVALID_KEY;
1128
+   case kHIDUsage_KeyboardInternational3:
1129
+       return INVALID_KEY;
1130
+   case kHIDUsage_KeyboardInternational4:
1131
+       return INVALID_KEY;
1132
+   case kHIDUsage_KeyboardInternational5:
1133
+       return INVALID_KEY;
1134
+   case kHIDUsage_KeyboardInternational6:
1135
+       return INVALID_KEY;
1136
+   case kHIDUsage_KeyboardInternational7:
1137
+       return INVALID_KEY;
1138
+   case kHIDUsage_KeyboardInternational8:
1139
+       return INVALID_KEY;
1140
+   case kHIDUsage_KeyboardInternational9:
1141
+       return INVALID_KEY;
1142
+
1143
+   case kHIDUsage_KeyboardLANG1:
1144
+       return INVALID_KEY;
1145
+   case kHIDUsage_KeyboardLANG2:
1146
+       return INVALID_KEY;
1147
+   case kHIDUsage_KeyboardLANG3:
1148
+       return INVALID_KEY;
1149
+   case kHIDUsage_KeyboardLANG4:
1150
+       return INVALID_KEY;
1151
+   case kHIDUsage_KeyboardLANG5:
1152
+       return INVALID_KEY;
1153
+   case kHIDUsage_KeyboardLANG6:
1154
+       return INVALID_KEY;
1155
+   case kHIDUsage_KeyboardLANG7:
1156
+       return INVALID_KEY;
1157
+   case kHIDUsage_KeyboardLANG8:
1158
+       return INVALID_KEY;
1159
+   case kHIDUsage_KeyboardLANG9:
1160
+       return INVALID_KEY;
1161
+
1162
+   case kHIDUsage_KeyboardAlternateErase:
1163
+       return INVALID_KEY;
1164
+   case kHIDUsage_KeyboardSysReqOrAttention:
1165
+       return INVALID_KEY;
1166
+   case kHIDUsage_KeyboardCancel:
1167
+       return INVALID_KEY;
1168
+   case kHIDUsage_KeyboardClear:
1169
+       return INVALID_KEY;
1170
+   case kHIDUsage_KeyboardPrior:
1171
+       return INVALID_KEY;
1172
+   case kHIDUsage_KeyboardReturn:
1173
+       return INVALID_KEY;
1174
+   case kHIDUsage_KeyboardSeparator:
1175
+       return INVALID_KEY;
1176
+   case kHIDUsage_KeyboardOut:
1177
+       return INVALID_KEY;
1178
+   case kHIDUsage_KeyboardOper:
1179
+       return INVALID_KEY;
1180
+   case kHIDUsage_KeyboardClearOrAgain:
1181
+       return INVALID_KEY;
1182
+   case kHIDUsage_KeyboardCrSelOrProps:
1183
+       return INVALID_KEY;
1184
+   case kHIDUsage_KeyboardExSel:
1185
+       return INVALID_KEY;
1186
+
1187
+       /* 0xa5-0xdf Reserved */
1188
+
1189
+   case kHIDUsage_KeyboardLeftControl:
1190
+       return kVK_Control;
1191
+   case kHIDUsage_KeyboardLeftShift:
1192
+       return kVK_Shift;
1193
+   case kHIDUsage_KeyboardLeftAlt:
1194
+       return kVK_Option;
1195
+   case kHIDUsage_KeyboardLeftGUI:
1196
+       return kVK_Command;
1197
+   case kHIDUsage_KeyboardRightControl:
1198
+       return kVK_RightControl;
1199
+   case kHIDUsage_KeyboardRightShift:
1200
+       return kVK_RightShift;
1201
+   case kHIDUsage_KeyboardRightAlt:
1202
+       return kVK_RightOption;
1203
+   case kHIDUsage_KeyboardRightGUI:
1204
+       return 0x36; //??
1205
+
1206
+       /* 0xe8-0xffff Reserved */
1207
+
1208
+   case kHIDUsage_Keyboard_Reserved:
1209
+       return INVALID_KEY;
1210
+   default:
1211
+       return INVALID_KEY;
1212
+   }
1213
+   return INVALID_KEY;
1214
+}
1215
+
1216
+obs_key_t obs_key_from_virtual_key(int code)
1217
+{
1218
+   switch (code) {
1219
+   case kVK_ANSI_A:
1220
+       return OBS_KEY_A;
1221
+   case kVK_ANSI_B:
1222
+       return OBS_KEY_B;
1223
+   case kVK_ANSI_C:
1224
+       return OBS_KEY_C;
1225
+   case kVK_ANSI_D:
1226
+       return OBS_KEY_D;
1227
+   case kVK_ANSI_E:
1228
+       return OBS_KEY_E;
1229
+   case kVK_ANSI_F:
1230
+       return OBS_KEY_F;
1231
+   case kVK_ANSI_G:
1232
+       return OBS_KEY_G;
1233
+   case kVK_ANSI_H:
1234
+       return OBS_KEY_H;
1235
+   case kVK_ANSI_I:
1236
+       return OBS_KEY_I;
1237
+   case kVK_ANSI_J:
1238
+       return OBS_KEY_J;
1239
+   case kVK_ANSI_K:
1240
+       return OBS_KEY_K;
1241
+   case kVK_ANSI_L:
1242
+       return OBS_KEY_L;
1243
+   case kVK_ANSI_M:
1244
+       return OBS_KEY_M;
1245
+   case kVK_ANSI_N:
1246
+       return OBS_KEY_N;
1247
+   case kVK_ANSI_O:
1248
+       return OBS_KEY_O;
1249
+   case kVK_ANSI_P:
1250
+       return OBS_KEY_P;
1251
+   case kVK_ANSI_Q:
1252
+       return OBS_KEY_Q;
1253
+   case kVK_ANSI_R:
1254
+       return OBS_KEY_R;
1255
+   case kVK_ANSI_S:
1256
+       return OBS_KEY_S;
1257
+   case kVK_ANSI_T:
1258
+       return OBS_KEY_T;
1259
+   case kVK_ANSI_U:
1260
+       return OBS_KEY_U;
1261
+   case kVK_ANSI_V:
1262
+       return OBS_KEY_V;
1263
+   case kVK_ANSI_W:
1264
+       return OBS_KEY_W;
1265
+   case kVK_ANSI_X:
1266
+       return OBS_KEY_X;
1267
+   case kVK_ANSI_Y:
1268
+       return OBS_KEY_Y;
1269
+   case kVK_ANSI_Z:
1270
+       return OBS_KEY_Z;
1271
+
1272
+   case kVK_ANSI_1:
1273
+       return OBS_KEY_1;
1274
+   case kVK_ANSI_2:
1275
+       return OBS_KEY_2;
1276
+   case kVK_ANSI_3:
1277
+       return OBS_KEY_3;
1278
+   case kVK_ANSI_4:
1279
+       return OBS_KEY_4;
1280
+   case kVK_ANSI_5:
1281
+       return OBS_KEY_5;
1282
+   case kVK_ANSI_6:
1283
+       return OBS_KEY_6;
1284
+   case kVK_ANSI_7:
1285
+       return OBS_KEY_7;
1286
+   case kVK_ANSI_8:
1287
+       return OBS_KEY_8;
1288
+   case kVK_ANSI_9:
1289
+       return OBS_KEY_9;
1290
+   case kVK_ANSI_0:
1291
+       return OBS_KEY_0;
1292
+
1293
+   case kVK_Return:
1294
+       return OBS_KEY_RETURN;
1295
+   case kVK_Escape:
1296
+       return OBS_KEY_ESCAPE;
1297
+   case kVK_Delete:
1298
+       return OBS_KEY_BACKSPACE;
1299
+   case kVK_Tab:
1300
+       return OBS_KEY_TAB;
1301
+   case kVK_Space:
1302
+       return OBS_KEY_SPACE;
1303
+   case kVK_ANSI_Minus:
1304
+       return OBS_KEY_MINUS;
1305
+   case kVK_ANSI_Equal:
1306
+       return OBS_KEY_EQUAL;
1307
+   case kVK_ANSI_LeftBracket:
1308
+       return OBS_KEY_BRACKETLEFT;
1309
+   case kVK_ANSI_RightBracket:
1310
+       return OBS_KEY_BRACKETRIGHT;
1311
+   case kVK_ANSI_Backslash:
1312
+       return OBS_KEY_BACKSLASH;
1313
+   case kVK_ANSI_Semicolon:
1314
+       return OBS_KEY_SEMICOLON;
1315
+   case kVK_ANSI_Quote:
1316
+       return OBS_KEY_QUOTE;
1317
+   case kVK_ANSI_Grave:
1318
+       return OBS_KEY_DEAD_GRAVE;
1319
+   case kVK_ANSI_Comma:
1320
+       return OBS_KEY_COMMA;
1321
+   case kVK_ANSI_Period:
1322
+       return OBS_KEY_PERIOD;
1323
+   case kVK_ANSI_Slash:
1324
+       return OBS_KEY_SLASH;
1325
+   case kVK_CapsLock:
1326
+       return OBS_KEY_CAPSLOCK;
1327
+   case kVK_ISO_Section:
1328
+       return OBS_KEY_SECTION;
1329
+
1330
+   case kVK_F1:
1331
+       return OBS_KEY_F1;
1332
+   case kVK_F2:
1333
+       return OBS_KEY_F2;
1334
+   case kVK_F3:
1335
+       return OBS_KEY_F3;
1336
+   case kVK_F4:
1337
+       return OBS_KEY_F4;
1338
+   case kVK_F5:
1339
+       return OBS_KEY_F5;
1340
+   case kVK_F6:
1341
+       return OBS_KEY_F6;
1342
+   case kVK_F7:
1343
+       return OBS_KEY_F7;
1344
+   case kVK_F8:
1345
+       return OBS_KEY_F8;
1346
+   case kVK_F9:
1347
+       return OBS_KEY_F9;
1348
+   case kVK_F10:
1349
+       return OBS_KEY_F10;
1350
+   case kVK_F11:
1351
+       return OBS_KEY_F11;
1352
+   case kVK_F12:
1353
+       return OBS_KEY_F12;
1354
+
1355
+   case kVK_Help:
1356
+       return OBS_KEY_HELP;
1357
+   case kVK_Home:
1358
+       return OBS_KEY_HOME;
1359
+   case kVK_PageUp:
1360
+       return OBS_KEY_PAGEUP;
1361
+   case kVK_ForwardDelete:
1362
+       return OBS_KEY_DELETE;
1363
+   case kVK_End:
1364
+       return OBS_KEY_END;
1365
+   case kVK_PageDown:
1366
+       return OBS_KEY_PAGEDOWN;
1367
+
1368
+   case kVK_RightArrow:
1369
+       return OBS_KEY_RIGHT;
1370
+   case kVK_LeftArrow:
1371
+       return OBS_KEY_LEFT;
1372
+   case kVK_DownArrow:
1373
+       return OBS_KEY_DOWN;
1374
+   case kVK_UpArrow:
1375
+       return OBS_KEY_UP;
1376
+
1377
+   case kVK_ANSI_KeypadClear:
1378
+       return OBS_KEY_CLEAR;
1379
+   case kVK_ANSI_KeypadDivide:
1380
+       return OBS_KEY_NUMSLASH;
1381
+   case kVK_ANSI_KeypadMultiply:
1382
+       return OBS_KEY_NUMASTERISK;
1383
+   case kVK_ANSI_KeypadMinus:
1384
+       return OBS_KEY_NUMMINUS;
1385
+   case kVK_ANSI_KeypadPlus:
1386
+       return OBS_KEY_NUMPLUS;
1387
+   case kVK_ANSI_KeypadEnter:
1388
+       return OBS_KEY_ENTER;
1389
+
1390
+   case kVK_ANSI_Keypad1:
1391
+       return OBS_KEY_NUM1;
1392
+   case kVK_ANSI_Keypad2:
1393
+       return OBS_KEY_NUM2;
1394
+   case kVK_ANSI_Keypad3:
1395
+       return OBS_KEY_NUM3;
1396
+   case kVK_ANSI_Keypad4:
1397
+       return OBS_KEY_NUM4;
1398
+   case kVK_ANSI_Keypad5:
1399
+       return OBS_KEY_NUM5;
1400
+   case kVK_ANSI_Keypad6:
1401
+       return OBS_KEY_NUM6;
1402
+   case kVK_ANSI_Keypad7:
1403
+       return OBS_KEY_NUM7;
1404
+   case kVK_ANSI_Keypad8:
1405
+       return OBS_KEY_NUM8;
1406
+   case kVK_ANSI_Keypad9:
1407
+       return OBS_KEY_NUM9;
1408
+   case kVK_ANSI_Keypad0:
1409
+       return OBS_KEY_NUM0;
1410
+
1411
+   case kVK_ANSI_KeypadDecimal:
1412
+       return OBS_KEY_NUMPERIOD;
1413
+   case kVK_ANSI_KeypadEquals:
1414
+       return OBS_KEY_NUMEQUAL;
1415
+
1416
+   case kVK_F13:
1417
+       return OBS_KEY_F13;
1418
+   case kVK_F14:
1419
+       return OBS_KEY_F14;
1420
+   case kVK_F15:
1421
+       return OBS_KEY_F15;
1422
+   case kVK_F16:
1423
+       return OBS_KEY_F16;
1424
+   case kVK_F17:
1425
+       return OBS_KEY_F17;
1426
+   case kVK_F18:
1427
+       return OBS_KEY_F18;
1428
+   case kVK_F19:
1429
+       return OBS_KEY_F19;
1430
+   case kVK_F20:
1431
+       return OBS_KEY_F20;
1432
+
1433
+   case kVK_Control:
1434
+       return OBS_KEY_CONTROL;
1435
+   case kVK_Shift:
1436
+       return OBS_KEY_SHIFT;
1437
+   case kVK_Option:
1438
+       return OBS_KEY_ALT;
1439
+   case kVK_Command:
1440
+       return OBS_KEY_META;
1441
+   case kVK_RightControl:
1442
+       return OBS_KEY_CONTROL;
1443
+   case kVK_RightShift:
1444
+       return OBS_KEY_SHIFT;
1445
+   case kVK_RightOption:
1446
+       return OBS_KEY_ALT;
1447
+   case 0x36:
1448
+       return OBS_KEY_META;
1449
+
1450
+   case kVK_Function:
1451
+   case kVK_Mute:
1452
+   case kVK_VolumeDown:
1453
+   case kVK_VolumeUp:
1454
+       break;
1455
+   }
1456
+   return OBS_KEY_NONE;
1457
+}
1458
+
1459
+static inline void load_key(obs_hotkeys_platform_t *plat, IOHIDElementRef key)
1460
+{
1461
+   UInt32 usage_code = IOHIDElementGetUsage(key);
1462
+   UInt16 carbon_code = usage_to_carbon(usage_code);
1463
+
1464
+   if (carbon_code == INVALID_KEY)
1465
+       return;
1466
+
1467
+   obs_key_t obs_key = obs_key_from_virtual_key(carbon_code);
1468
+   if (obs_key == OBS_KEY_NONE)
1469
+       return;
1470
+
1471
+   da_push_back(plat->keys[obs_key], &key);
1472
+   CFRetain(*(IOHIDElementRef *)da_end(plat->keys[obs_key]));
1473
+}
1474
+
1475
+static inline void load_keyboard(obs_hotkeys_platform_t *plat,
1476
+                IOHIDDeviceRef keyboard)
1477
+{
1478
+   CFArrayRef keys = IOHIDDeviceCopyMatchingElements(
1479
+       keyboard, NULL, kIOHIDOptionsTypeNone);
1480
+
1481
+   if (!keys) {
1482
+       blog(LOG_ERROR, "hotkeys-cocoa: Getting keyboard keys failed");
1483
+       return;
1484
+   }
1485
+
1486
+   CFIndex count = CFArrayGetCount(keys);
1487
+   if (!count) {
1488
+       blog(LOG_ERROR, "hotkeys-cocoa: Keyboard has no keys");
1489
+       CFRelease(keys);
1490
+       return;
1491
+   }
1492
+
1493
+   for (CFIndex i = 0; i < count; i++) {
1494
+       IOHIDElementRef key =
1495
+           (IOHIDElementRef)CFArrayGetValueAtIndex(keys, i);
1496
+
1497
+       // Skip non-matching keys elements
1498
+       if (IOHIDElementGetUsagePage(key) != kHIDPage_KeyboardOrKeypad)
1499
+           continue;
1500
+
1501
+       load_key(plat, key);
1502
+   }
1503
+
1504
+   CFRelease(keys);
1505
+}
1506
+
1507
+static bool init_keyboard(obs_hotkeys_platform_t *plat)
1508
+{
1509
+   CFSetRef keyboards = copy_devices(plat, kHIDPage_GenericDesktop,
1510
+                     kHIDUsage_GD_Keyboard);
1511
+   if (!keyboards)
1512
+       return false;
1513
+
1514
+   CFIndex count = CFSetGetCount(keyboards);
1515
+
1516
+   CFTypeRef devices[count];
1517
+   CFSetGetValues(keyboards, devices);
1518
+
1519
+   for (CFIndex i = 0; i < count; i++)
1520
+       load_keyboard(plat, (IOHIDDeviceRef)devices[i]);
1521
+
1522
+   CFRelease(keyboards);
1523
+   return true;
1524
+}
1525
+
1526
+static inline void free_hotkeys_platform(obs_hotkeys_platform_t *plat)
1527
+{
1528
+   if (!plat)
1529
+       return;
1530
+
1531
+   if (plat->tis) {
1532
+       CFRelease(plat->tis);
1533
+       plat->tis = NULL;
1534
+   }
1535
+
1536
+   if (plat->layout_data) {
1537
+       CFRelease(plat->layout_data);
1538
+       plat->layout_data = NULL;
1539
+   }
1540
+
1541
+   if (plat->manager) {
1542
+       CFRelease(plat->manager);
1543
+       plat->manager = NULL;
1544
+   }
1545
+
1546
+   for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
1547
+       for (size_t j = 0; j < plat->keys[i].num; j++)
1548
+           CFRelease(plat->keys[i].array[j]);
1549
+
1550
+       da_free(plat->keys[i]);
1551
+   }
1552
+
1553
+   bfree(plat);
1554
+}
1555
+
1556
+static bool log_layout_name(TISInputSourceRef tis)
1557
+{
1558
+   struct dstr layout_name = {0};
1559
+   CFStringRef sid = (CFStringRef)TISGetInputSourceProperty(
1560
+       tis, kTISPropertyInputSourceID);
1561
+   if (!sid) {
1562
+       blog(LOG_ERROR, "hotkeys-cocoa: Failed getting InputSourceID");
1563
+       return false;
1564
+   }
1565
+
1566
+   if (!dstr_from_cfstring(&layout_name, sid)) {
1567
+       blog(LOG_ERROR, "hotkeys-cocoa: Could not convert InputSourceID"
1568
+               " to CString");
1569
+       goto fail;
1570
+   }
1571
+
1572
+   blog(LOG_INFO, "hotkeys-cocoa: Using layout '%s'", layout_name.array);
1573
+
1574
+   dstr_free(&layout_name);
1575
+   return true;
1576
+
1577
+fail:
1578
+   dstr_free(&layout_name);
1579
+   return false;
1580
+}
1581
+
1582
+static bool init_hotkeys_platform(obs_hotkeys_platform_t **plat_)
1583
+{
1584
+   if (!plat_)
1585
+       return false;
1586
+
1587
+   *plat_ = bzalloc(sizeof(obs_hotkeys_platform_t));
1588
+   obs_hotkeys_platform_t *plat = *plat_;
1589
+   if (!plat) {
1590
+       *plat_ = NULL;
1591
+       return false;
1592
+   }
1593
+
1594
+   plat->tis = TISCopyCurrentKeyboardLayoutInputSource();
1595
+   plat->layout_data = (CFDataRef)TISGetInputSourceProperty(
1596
+       plat->tis, kTISPropertyUnicodeKeyLayoutData);
1597
+
1598
+   if (!plat->layout_data) {
1599
+       blog(LOG_ERROR, "hotkeys-cocoa: Failed getting LayoutData");
1600
+       goto fail;
1601
+   }
1602
+
1603
+   CFRetain(plat->layout_data);
1604
+   plat->layout = (UCKeyboardLayout *)CFDataGetBytePtr(plat->layout_data);
1605
+
1606
+   plat->manager =
1607
+       IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
1608
+
1609
+   IOReturn openStatus =
1610
+       IOHIDManagerOpen(plat->manager, kIOHIDOptionsTypeNone);
1611
+   if (openStatus != kIOReturnSuccess) {
1612
+       blog(LOG_ERROR, "hotkeys-cocoa: Failed opening HIDManager");
1613
+       goto fail;
1614
+   }
1615
+
1616
+   init_keyboard(plat);
1617
+
1618
+   return true;
1619
+
1620
+fail:
1621
+   hotkeys_release(plat);
1622
+   *plat_ = NULL;
1623
+   return false;
1624
+}
1625
+
1626
+static void input_method_changed(CFNotificationCenterRef nc, void *observer,
1627
+                CFStringRef name, const void *object,
1628
+                CFDictionaryRef user_info)
1629
+{
1630
+   UNUSED_PARAMETER(nc);
1631
+   UNUSED_PARAMETER(name);
1632
+   UNUSED_PARAMETER(object);
1633
+   UNUSED_PARAMETER(user_info);
1634
+
1635
+   struct obs_core_hotkeys *hotkeys = observer;
1636
+   obs_hotkeys_platform_t *new_plat;
1637
+
1638
+   if (init_hotkeys_platform(&new_plat)) {
1639
+       obs_hotkeys_platform_t *plat;
1640
+
1641
+       pthread_mutex_lock(&hotkeys->mutex);
1642
+       plat = hotkeys->platform_context;
1643
+
1644
+       if (new_plat && plat &&
1645
+           new_plat->layout_data == plat->layout_data) {
1646
+           pthread_mutex_unlock(&hotkeys->mutex);
1647
+           hotkeys_release(new_plat);
1648
+           return;
1649
+       }
1650
+
1651
+       hotkeys->platform_context = new_plat;
1652
+       if (new_plat)
1653
+           log_layout_name(new_plat->tis);
1654
+       pthread_mutex_unlock(&hotkeys->mutex);
1655
+
1656
+       calldata_t params = {0};
1657
+       signal_handler_signal(hotkeys->signals, "hotkey_layout_change",
1658
+                     &params);
1659
+       if (plat)
1660
+           hotkeys_release(plat);
1661
+   }
1662
+}
1663
+
1664
+bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
1665
+{
1666
+   CFNotificationCenterAddObserver(
1667
+       CFNotificationCenterGetDistributedCenter(), hotkeys,
1668
+       input_method_changed,
1669
+       kTISNotifySelectedKeyboardInputSourceChanged, NULL,
1670
+       CFNotificationSuspensionBehaviorDeliverImmediately);
1671
+
1672
+   input_method_changed(NULL, hotkeys, NULL, NULL, NULL);
1673
+   return hotkeys->platform_context != NULL;
1674
+}
1675
+
1676
+void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
1677
+{
1678
+   CFNotificationCenterRemoveEveryObserver(
1679
+       CFNotificationCenterGetDistributedCenter(), hotkeys);
1680
+
1681
+   hotkeys_release(hotkeys->platform_context);
1682
+}
1683
+
1684
+typedef unsigned long NSUInteger;
1685
+static bool mouse_button_pressed(obs_key_t key, bool *pressed)
1686
+{
1687
+   int button = 0;
1688
+   switch (key) {
1689
+#define MAP_BUTTON(n)           \
1690
+   case OBS_KEY_MOUSE##n:  \
1691
+       button = n - 1; \
1692
+       break
1693
+       MAP_BUTTON(1);
1694
+       MAP_BUTTON(2);
1695
+       MAP_BUTTON(3);
1696
+       MAP_BUTTON(4);
1697
+       MAP_BUTTON(5);
1698
+       MAP_BUTTON(6);
1699
+       MAP_BUTTON(7);
1700
+       MAP_BUTTON(8);
1701
+       MAP_BUTTON(9);
1702
+       MAP_BUTTON(10);
1703
+       MAP_BUTTON(11);
1704
+       MAP_BUTTON(12);
1705
+       MAP_BUTTON(13);
1706
+       MAP_BUTTON(14);
1707
+       MAP_BUTTON(15);
1708
+       MAP_BUTTON(16);
1709
+       MAP_BUTTON(17);
1710
+       MAP_BUTTON(18);
1711
+       MAP_BUTTON(19);
1712
+       MAP_BUTTON(20);
1713
+       MAP_BUTTON(21);
1714
+       MAP_BUTTON(22);
1715
+       MAP_BUTTON(23);
1716
+       MAP_BUTTON(24);
1717
+       MAP_BUTTON(25);
1718
+       MAP_BUTTON(26);
1719
+       MAP_BUTTON(27);
1720
+       MAP_BUTTON(28);
1721
+       MAP_BUTTON(29);
1722
+       break;
1723
+#undef MAP_BUTTON
1724
+
1725
+   default:
1726
+       return false;
1727
+   }
1728
+
1729
+   Class NSEvent = objc_getClass("NSEvent");
1730
+   SEL pressedMouseButtonsSel = sel_registerName("pressedMouseButtons");
1731
+   typedef int (*func)(id, SEL);
1732
+   func pressedMouseButtons = (func)objc_msgSend;
1733
+   NSUInteger buttons = (NSUInteger)pressedMouseButtons(
1734
+       (id)NSEvent, pressedMouseButtonsSel);
1735
+
1736
+   *pressed = (buttons & (1 << button)) != 0;
1737
+   return true;
1738
+}
1739
+
1740
+bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *plat,
1741
+                    obs_key_t key)
1742
+{
1743
+   bool mouse_pressed = false;
1744
+   if (mouse_button_pressed(key, &mouse_pressed))
1745
+       return mouse_pressed;
1746
+
1747
+   if (!plat)
1748
+       return false;
1749
+
1750
+   if (key >= OBS_KEY_LAST_VALUE)
1751
+       return false;
1752
+
1753
+   for (size_t i = 0; i < plat->keys[key].num;) {
1754
+       IOHIDElementRef element = plat->keys[key].array[i];
1755
+       IOHIDValueRef value = 0;
1756
+       IOHIDDeviceRef device = IOHIDElementGetDevice(element);
1757
+
1758
+       if (IOHIDDeviceGetValue(device, element, &value) !=
1759
+           kIOReturnSuccess) {
1760
+           i += 1;
1761
+           continue;
1762
+       }
1763
+
1764
+       if (!value) {
1765
+           CFRelease(element);
1766
+           da_erase(plat->keys[key], i);
1767
+           continue;
1768
+       }
1769
+
1770
+       if (IOHIDValueGetIntegerValue(value) == 1)
1771
+           return true;
1772
+
1773
+       i += 1;
1774
+   }
1775
+
1776
+   return false;
1777
+}
1778
obs-studio-24.0.3.tar.xz/libobs/obs-config.h -> obs-studio-24.0.5.tar.xz/libobs/obs-config.h Changed
10
 
1
@@ -41,7 +41,7 @@
2
  *
3
  * Reset to zero each major or minor version
4
  */
5
-#define LIBOBS_API_PATCH_VER 3
6
+#define LIBOBS_API_PATCH_VER 5
7
 
8
 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \
9
    ((major << 24) | (minor << 16) | patch)
10
obs-studio-24.0.3.tar.xz/libobs/obs-video-gpu-encode.c -> obs-studio-24.0.5.tar.xz/libobs/obs-video-gpu-encode.c Changed
15
 
1
@@ -58,9 +58,10 @@
2
        video_output_inc_texture_frames(video->video);
3
 
4
        for (size_t i = 0; i < video->gpu_encoders.num; i++) {
5
-           obs_encoder_t *encoder = video->gpu_encoders.array[i];
6
-           da_push_back(encoders, &encoder);
7
-           obs_encoder_addref(encoder);
8
+           obs_encoder_t *encoder = obs_encoder_get_ref(
9
+               video->gpu_encoders.array[i]);
10
+           if (encoder)
11
+               da_push_back(encoders, &encoder);
12
        }
13
 
14
        pthread_mutex_unlock(&video->gpu_encoder_mutex);
15
obs-studio-24.0.3.tar.xz/plugins/linux-capture/xcompcap-main.cpp -> obs-studio-24.0.5.tar.xz/plugins/linux-capture/xcompcap-main.cpp Changed
11
 
1
@@ -664,7 +664,8 @@
2
        effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
3
 
4
        while (gs_effect_loop(effect, "Draw")) {
5
-           xcursor_render(p->cursor);
6
+           xcursor_render(p->cursor, -p->cur_cut_left,
7
+                      -p->cur_cut_top);
8
        }
9
    }
10
 }
11
obs-studio-24.0.3.tar.xz/plugins/linux-capture/xcursor.c -> obs-studio-24.0.5.tar.xz/plugins/linux-capture/xcursor.c Changed
20
 
1
@@ -103,7 +103,7 @@
2
    XFree(xc);
3
 }
4
 
5
-void xcursor_render(xcursor_t *data)
6
+void xcursor_render(xcursor_t *data, int x_offset, int y_offset)
7
 {
8
    if (!data->tex)
9
        return;
10
@@ -117,7 +117,8 @@
11
    gs_enable_color(true, true, true, false);
12
 
13
    gs_matrix_push();
14
-   gs_matrix_translate3f(data->render_x, data->render_y, 0.0f);
15
+   gs_matrix_translate3f(data->render_x + x_offset,
16
+                 data->render_y + y_offset, 0.0f);
17
    gs_draw_sprite(data->tex, 0, 0, 0);
18
    gs_matrix_pop();
19
 
20
obs-studio-24.0.3.tar.xz/plugins/linux-capture/xcursor.h -> obs-studio-24.0.5.tar.xz/plugins/linux-capture/xcursor.h Changed
10
 
1
@@ -61,7 +61,7 @@
2
  *
3
  * This needs to be executed within a valid render context
4
  */
5
-void xcursor_render(xcursor_t *data);
6
+void xcursor_render(xcursor_t *data, int x_offset, int y_offset);
7
 
8
 /**
9
  * Specify offset for the cursor
10
obs-studio-24.0.3.tar.xz/plugins/linux-capture/xhelpers.c -> obs-studio-24.0.5.tar.xz/plugins/linux-capture/xhelpers.c Changed
9
 
1
@@ -108,7 +108,6 @@
2
 {
3
    if (!xcb)
4
        return 0;
5
-
6
    xcb_screen_t *screen;
7
    screen = xcb_setup_roots_iterator(xcb_get_setup(xcb)).data;
8
 
9
obs-studio-24.0.3.tar.xz/plugins/linux-capture/xshm-input.c -> obs-studio-24.0.5.tar.xz/plugins/linux-capture/xshm-input.c Changed
12
 
1
@@ -324,7 +324,9 @@
2
                ")",
3
                i, w, h, x, y);
4
 
5
-       obs_property_list_add_int(screens, screen_info.array, i);
6
+       if (h > 0 && w > 0)
7
+           obs_property_list_add_int(screens, screen_info.array,
8
+                         i);
9
    }
10
 
11
    /* handle missing screen */
12
obs-studio-24.0.3.tar.xz/plugins/obs-ffmpeg/obs-ffmpeg-output.c -> obs-studio-24.0.5.tar.xz/plugins/obs-ffmpeg/obs-ffmpeg-output.c Changed
11
 
1
@@ -183,7 +183,8 @@
2
            *assign = 0;
3
            value = assign + 1;
4
 
5
-           if (av_opt_set(context->priv_data, name, value, 0)) {
6
+           if (av_opt_set(context, name, value,
7
+                      AV_OPT_SEARCH_CHILDREN)) {
8
                blog(LOG_WARNING, "Failed to set %s=%s", name,
9
                     value);
10
                ret = false;
11
obs-studio-24.0.3.tar.xz/plugins/obs-outputs/rtmp-stream.c -> obs-studio-24.0.5.tar.xz/plugins/obs-outputs/rtmp-stream.c Changed
10
 
1
@@ -1342,7 +1342,7 @@
2
            return;
3
        }
4
 
5
-       if (buffer_duration_usec >= DBR_TRIGGER_USEC) {
6
+       if ((uint64_t)buffer_duration_usec >= DBR_TRIGGER_USEC) {
7
            pthread_mutex_lock(&stream->dbr_mutex);
8
            bitrate_changed = dbr_bitrate_lowered(stream);
9
            pthread_mutex_unlock(&stream->dbr_mutex);
10
obs-studio-24.0.3.tar.xz/plugins/rtmp-services/CMakeLists.txt -> obs-studio-24.0.5.tar.xz/plugins/rtmp-services/CMakeLists.txt Changed
37
 
1
@@ -1,15 +1,21 @@
2
 project(rtmp-services)
3
 
4
+find_package(Libcurl REQUIRED)
5
+
6
+include_directories(${LIBCURL_INCLUDE_DIRS})
7
+
8
 include_directories(${OBS_JANSSON_INCLUDE_DIRS})
9
 
10
 set(rtmp-services_SOURCES
11
    twitch.c
12
+   younow.c
13
    rtmp-common.c
14
    rtmp-custom.c
15
    rtmp-services-main.c)
16
 
17
 set(rtmp-services_HEADERS
18
    twitch.h
19
+   younow.h
20
    rtmp-format-ver.h)
21
 
22
 set(RTMP_SERVICES_URL
23
@@ -28,10 +34,12 @@
24
    ${rtmp-services_SOURCES}
25
    ${rtmp-services_HEADERS}
26
    ${rtmp-services_config_HEADERS})
27
+
28
 target_link_libraries(rtmp-services
29
    libobs
30
    file-updater
31
-   ${OBS_JANSSON_IMPORT})
32
+   ${OBS_JANSSON_IMPORT}
33
+   ${LIBCURL_LIBRARIES})
34
 
35
 target_include_directories(rtmp-services
36
    PUBLIC
37
obs-studio-24.0.3.tar.xz/plugins/rtmp-services/data/package.json -> obs-studio-24.0.5.tar.xz/plugins/rtmp-services/data/package.json Changed
14
 
1
@@ -1,10 +1,10 @@
2
 {
3
    "url": "https://obsproject.com/obs2_update/rtmp-services",
4
-   "version": 112,
5
+   "version": 114,
6
    "files": [
7
        {
8
            "name": "services.json",
9
-           "version": 112
10
+           "version": 114
11
        }
12
    ]
13
 }
14
obs-studio-24.0.3.tar.xz/plugins/rtmp-services/data/services.json -> obs-studio-24.0.5.tar.xz/plugins/rtmp-services/data/services.json Changed
71
 
1
@@ -1466,6 +1466,20 @@
2
             }
3
         },
4
         {
5
+            "name": "ChathostessModels",
6
+            "servers": [
7
+                {
8
+                    "name": "ChathostessModels - Default",
9
+                    "url": "rtmp://wowza01.foobarweb.com/cmschatsys_video"
10
+                }
11
+            ],
12
+            "recommended": {
13
+                "keyint": 2,
14
+                "max video bitrate": 3000,
15
+                "max audio bitrate": 128
16
+            }
17
+        },
18
+        {
19
             "name": "Camplace",
20
             "servers": [
21
                 {
22
@@ -1501,6 +1515,24 @@
23
             }
24
         },
25
         {
26
+            "name": "YouNow",
27
+            "common": false,
28
+            "servers": [
29
+                {
30
+                    "name": "younow.com",
31
+                    "url": "https://signaling-api.younow-prod.video.propsproject.com/api/v1/ingest/server/"
32
+                }
33
+            ],
34
+            "recommended": {
35
+                "keyint": 2,
36
+                "output": "ftl_output",
37
+                "max audio bitrate": 160,
38
+                "max video bitrate": 7000,
39
+                "profile": "main",
40
+                "bframes": 0
41
+            }
42
+        },
43
+        {
44
             "name": "Steam",
45
             "common": false,
46
             "servers": [
47
@@ -1559,6 +1591,23 @@
48
                 "max video bitrate": 7000,
49
                 "max audio bitrate": 128
50
             }
51
+        },
52
+        {
53
+            "name": "Stars.AVN.com",
54
+            "servers": [
55
+                {
56
+                    "name": "Default",
57
+                    "url": "rtmp://alpha.gateway.stars.avn.com/live"
58
+                }
59
+            ],
60
+            "recommended": {
61
+                "keyint": 2,
62
+                "profile": "main",
63
+                "max video bitrate": 2500,
64
+                "max audio bitrate": 192,
65
+                "bframes": 0,
66
+                "x264opts": "tune=zerolatency"
67
+            }
68
         }
69
     ]
70
 }
71
obs-studio-24.0.3.tar.xz/plugins/rtmp-services/rtmp-common.c -> obs-studio-24.0.5.tar.xz/plugins/rtmp-services/rtmp-common.c Changed
33
 
1
@@ -5,6 +5,7 @@
2
 
3
 #include "rtmp-format-ver.h"
4
 #include "twitch.h"
5
+#include "younow.h"
6
 
7
 struct rtmp_common {
8
    char *service;
9
@@ -485,7 +486,9 @@
10
    obs_data_set_string(settings, "rate_control", "CBR");
11
 
12
    item = json_object_get(recommended, "profile");
13
-   if (json_is_string(item)) {
14
+   obs_data_item_t *enc_item = obs_data_item_byname(settings, "profile");
15
+   if (json_is_string(item) &&
16
+       obs_data_item_gettype(enc_item) == OBS_DATA_STRING) {
17
        const char *profile = json_string_value(item);
18
        obs_data_set_string(settings, "profile", profile);
19
    }
20
@@ -594,6 +597,12 @@
21
        }
22
    }
23
 
24
+   if (service->service && strcmp(service->service, "YouNow") == 0) {
25
+       if (service->server && service->key) {
26
+           return younow_get_ingest(service->server, service->key);
27
+       }
28
+   }
29
+
30
    return service->server;
31
 }
32
 
33
obs-studio-24.0.5.tar.xz/plugins/rtmp-services/younow.c Added
115
 
1
@@ -0,0 +1,113 @@
2
+#include <curl/curl.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+
6
+#include <util/dstr.h>
7
+#include "util/base.h"
8
+#include "younow.h"
9
+
10
+struct younow_mem_struct {
11
+   char *memory;
12
+   size_t size;
13
+};
14
+
15
+static char *current_ingest = NULL;
16
+
17
+static size_t younow_write_cb(void *contents, size_t size, size_t nmemb,
18
+                 void *userp)
19
+{
20
+   size_t realsize = size * nmemb;
21
+   struct younow_mem_struct *mem = (struct younow_mem_struct *)userp;
22
+
23
+   mem->memory = realloc(mem->memory, mem->size + realsize + 1);
24
+   if (mem->memory == NULL) {
25
+       blog(LOG_WARNING, "yyounow_write_cb: realloc returned NULL");
26
+       return 0;
27
+   }
28
+
29
+   memcpy(&(mem->memory[mem->size]), contents, realsize);
30
+   mem->size += realsize;
31
+   mem->memory[mem->size] = 0;
32
+
33
+   return realsize;
34
+}
35
+
36
+const char *younow_get_ingest(const char *server, const char *key)
37
+{
38
+   CURL *curl_handle;
39
+   CURLcode res;
40
+   struct younow_mem_struct chunk;
41
+   struct dstr uri;
42
+   long response_code;
43
+
44
+   // find the delimiter in stream key
45
+   const char *delim = strchr(key, '_');
46
+   if (delim == NULL) {
47
+       blog(LOG_WARNING,
48
+            "younow_get_ingest: delimiter not found in stream key");
49
+       return server;
50
+   }
51
+
52
+   curl_handle = curl_easy_init();
53
+
54
+   chunk.memory = malloc(1); /* will be grown as needed by realloc */
55
+   chunk.size = 0;           /* no data at this point */
56
+
57
+   dstr_init(&uri);
58
+   dstr_copy(&uri, server);
59
+   dstr_ncat(&uri, key, delim - key);
60
+
61
+   curl_easy_setopt(curl_handle, CURLOPT_URL, uri.array);
62
+   curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, true);
63
+   curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L);
64
+   curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 3L);
65
+   curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, younow_write_cb);
66
+   curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
67
+
68
+#if LIBCURL_VERSION_NUM >= 0x072400
69
+   // A lot of servers don't yet support ALPN
70
+   curl_easy_setopt(curl_handle, CURLOPT_SSL_ENABLE_ALPN, 0);
71
+#endif
72
+
73
+   res = curl_easy_perform(curl_handle);
74
+   dstr_free(&uri);
75
+
76
+   if (res != CURLE_OK) {
77
+       blog(LOG_WARNING,
78
+            "younow_get_ingest: curl_easy_perform() failed: %s",
79
+            curl_easy_strerror(res));
80
+       curl_easy_cleanup(curl_handle);
81
+       free(chunk.memory);
82
+       return server;
83
+   }
84
+
85
+   curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code);
86
+   if (response_code != 200) {
87
+       blog(LOG_WARNING,
88
+            "younow_get_ingest: curl_easy_perform() returned code: %ld",
89
+            response_code);
90
+       curl_easy_cleanup(curl_handle);
91
+       free(chunk.memory);
92
+       return server;
93
+   }
94
+
95
+   curl_easy_cleanup(curl_handle);
96
+
97
+   if (chunk.size == 0) {
98
+       blog(LOG_WARNING,
99
+            "younow_get_ingest: curl_easy_perform() returned empty response");
100
+       free(chunk.memory);
101
+       return server;
102
+   }
103
+
104
+   if (current_ingest) {
105
+       free(current_ingest);
106
+       current_ingest = NULL;
107
+   }
108
+
109
+   current_ingest = strdup(chunk.memory);
110
+   free(chunk.memory);
111
+   blog(LOG_INFO, "younow_get_ingest: returning ingest: %s",
112
+        current_ingest);
113
+   return current_ingest;
114
+}
115
obs-studio-24.0.5.tar.xz/plugins/rtmp-services/younow.h Added
6
 
1
@@ -0,0 +1,3 @@
2
+#pragma once
3
+
4
+extern const char *younow_get_ingest(const char *server, const char *key);
5
\ No newline at end of file
6
obs-studio-24.0.3.tar.xz/plugins/win-capture/window-helpers.c -> obs-studio-24.0.5.tar.xz/plugins/win-capture/window-helpers.c Changed
25
 
1
@@ -128,12 +128,23 @@
2
 
3
 /* not capturable or internal windows */
4
 static const char *internal_microsoft_exes[] = {
5
+   "startmenuexperiencehost",
6
    "applicationframehost",
7
+   "peopleexperiencehost",
8
    "shellexperiencehost",
9
+   "microsoft.notes",
10
    "windowsinternal",
11
+   "systemsettings",
12
+   "textinputhost",
13
    "winstore.app",
14
+   "searchapp",
15
+   "video.ui",
16
    "searchui",
17
    "lockapp",
18
+   "cortana",
19
+   "gamebar",
20
+   "tabtip",
21
+   "time",
22
    NULL,
23
 };
24
 
25
obs-studio-24.0.3.tar.xz/plugins/win-dshow/win-dshow.cpp -> obs-studio-24.0.5.tar.xz/plugins/win-dshow/win-dshow.cpp Changed
14
 
1
@@ -468,6 +468,12 @@
2
 void DShowInput::OnEncodedVideoData(enum AVCodecID id, unsigned char *data,
3
                    size_t size, long long ts)
4
 {
5
+   /* If format changes, free and allow it to recreate the decoder */
6
+   if (ffmpeg_decode_valid(video_decoder) &&
7
+       video_decoder->codec->id != id) {
8
+       ffmpeg_decode_free(video_decoder);
9
+   }
10
+
11
    if (!ffmpeg_decode_valid(video_decoder)) {
12
        /* Only use MJPEG hardware decoding on resolutions higher
13
         * than 1920x1080.  The reason why is because we want to strike
14
obs-studio-24.0.3.tar.xz/plugins/win-wasapi/enum-wasapi.hpp -> obs-studio-24.0.5.tar.xz/plugins/win-wasapi/enum-wasapi.hpp Changed
9
 
1
@@ -2,6 +2,7 @@
2
 
3
 #define WIN32_MEAN_AND_LEAN
4
 #include <windows.h>
5
+#include <initguid.h>
6
 #include <mmdeviceapi.h>
7
 #include <audioclient.h>
8
 #include <propsys.h>
9
obs-studio-24.0.3.tar.xz/plugins/win-wasapi/win-wasapi.cpp -> obs-studio-24.0.5.tar.xz/plugins/win-wasapi/win-wasapi.cpp Changed
42
 
1
@@ -28,6 +28,7 @@
2
    obs_source_t *source;
3
    string device_id;
4
    string device_name;
5
+   string device_sample = "-";
6
    bool isInputDevice;
7
    bool useDeviceTiming = false;
8
    bool isDefaultDevice = false;
9
@@ -289,7 +290,8 @@
10
    client->Start();
11
    active = true;
12
 
13
-   blog(LOG_INFO, "WASAPI: Device '%s' initialized", device_name.c_str());
14
+   blog(LOG_INFO, "WASAPI: Device '%s' [%s Hz] initialized",
15
+        device_name.c_str(), device_sample.c_str());
16
 }
17
 
18
 void WASAPISource::Initialize()
19
@@ -308,6 +310,22 @@
20
 
21
    device_name = GetDeviceName(device);
22
 
23
+   HRESULT resSample;
24
+   IPropertyStore *store = nullptr;
25
+   PWAVEFORMATEX deviceFormatProperties;
26
+   PROPVARIANT prop;
27
+   resSample = device->OpenPropertyStore(STGM_READ, &store);
28
+   if (!FAILED(resSample)) {
29
+       resSample =
30
+           store->GetValue(PKEY_AudioEngine_DeviceFormat, &prop);
31
+       if (!FAILED(resSample)) {
32
+           deviceFormatProperties =
33
+               (PWAVEFORMATEX)prop.blob.pBlobData;
34
+           device_sample = std::to_string(
35
+               deviceFormatProperties->nSamplesPerSec);
36
+       }
37
+   }
38
+
39
    InitClient();
40
    if (!isInputDevice)
41
        InitRender();
42