1 /******************************************************************************
2 Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 ******************************************************************************/
17
18 #include "util/windows/win-registry.h"
19 #include "util/windows/win-version.h"
20 #include "util/platform.h"
21 #include "util/dstr.h"
22 #include "obs.h"
23 #include "obs-internal.h"
24
25 #include <windows.h>
26 #include <wscapi.h>
27 #include <iwscapi.h>
28
29 static uint32_t win_ver = 0;
30
get_module_extension(void)31 const char *get_module_extension(void)
32 {
33 return ".dll";
34 }
35
36 #ifdef _WIN64
37 #define BIT_STRING "64bit"
38 #else
39 #define BIT_STRING "32bit"
40 #endif
41
42 static const char *module_bin[] = {
43 "../../obs-plugins/" BIT_STRING,
44 };
45
46 static const char *module_data[] = {"../../data/obs-plugins/%module%"};
47
48 static const int module_patterns_size =
49 sizeof(module_bin) / sizeof(module_bin[0]);
50
add_default_module_paths(void)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 /* on windows, points to [base directory]/data/libobs */
find_libobs_data_file(const char * file)58 char *find_libobs_data_file(const char *file)
59 {
60 struct dstr path;
61 dstr_init(&path);
62
63 if (check_path(file, "../../data/libobs/", &path))
64 return path.array;
65
66 dstr_free(&path);
67 return NULL;
68 }
69
log_processor_info(void)70 static void log_processor_info(void)
71 {
72 HKEY key;
73 wchar_t data[1024];
74 char *str = NULL;
75 DWORD size, speed;
76 LSTATUS status;
77
78 memset(data, 0, sizeof(data));
79
80 status = RegOpenKeyW(
81 HKEY_LOCAL_MACHINE,
82 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &key);
83 if (status != ERROR_SUCCESS)
84 return;
85
86 size = sizeof(data);
87 status = RegQueryValueExW(key, L"ProcessorNameString", NULL, NULL,
88 (LPBYTE)data, &size);
89 if (status == ERROR_SUCCESS) {
90 os_wcs_to_utf8_ptr(data, 0, &str);
91 blog(LOG_INFO, "CPU Name: %s", str);
92 bfree(str);
93 }
94
95 size = sizeof(speed);
96 status = RegQueryValueExW(key, L"~MHz", NULL, NULL, (LPBYTE)&speed,
97 &size);
98 if (status == ERROR_SUCCESS)
99 blog(LOG_INFO, "CPU Speed: %ldMHz", speed);
100
101 RegCloseKey(key);
102 }
103
log_processor_cores(void)104 static void log_processor_cores(void)
105 {
106 blog(LOG_INFO, "Physical Cores: %d, Logical Cores: %d",
107 os_get_physical_cores(), os_get_logical_cores());
108 }
109
log_available_memory(void)110 static void log_available_memory(void)
111 {
112 MEMORYSTATUSEX ms;
113 ms.dwLength = sizeof(ms);
114
115 GlobalMemoryStatusEx(&ms);
116
117 #ifdef _WIN64
118 const char *note = "";
119 #else
120 const char *note = " (NOTE: 32bit programs cannot use more than 3gb)";
121 #endif
122
123 blog(LOG_INFO, "Physical Memory: %luMB Total, %luMB Free%s",
124 (DWORD)(ms.ullTotalPhys / 1048576),
125 (DWORD)(ms.ullAvailPhys / 1048576), note);
126 }
127
128 extern const char *get_win_release_id();
129
log_windows_version(void)130 static void log_windows_version(void)
131 {
132 struct win_version_info ver;
133 get_win_ver(&ver);
134
135 const char *release_id = get_win_release_id();
136
137 bool b64 = is_64_bit_windows();
138 const char *windows_bitness = b64 ? "64" : "32";
139
140 blog(LOG_INFO,
141 "Windows Version: %d.%d Build %d (release: %s; revision: %d; %s-bit)",
142 ver.major, ver.minor, ver.build, release_id, ver.revis,
143 windows_bitness);
144 }
145
log_admin_status(void)146 static void log_admin_status(void)
147 {
148 SID_IDENTIFIER_AUTHORITY auth = SECURITY_NT_AUTHORITY;
149 PSID admin_group;
150 BOOL success;
151
152 success = AllocateAndInitializeSid(&auth, 2,
153 SECURITY_BUILTIN_DOMAIN_RID,
154 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0,
155 0, 0, &admin_group);
156 if (success) {
157 if (!CheckTokenMembership(NULL, admin_group, &success))
158 success = false;
159 FreeSid(admin_group);
160 }
161
162 blog(LOG_INFO, "Running as administrator: %s",
163 success ? "true" : "false");
164 }
165
166 typedef HRESULT(WINAPI *dwm_is_composition_enabled_t)(BOOL *);
167
log_aero(void)168 static void log_aero(void)
169 {
170 dwm_is_composition_enabled_t composition_enabled = NULL;
171
172 const char *aeroMessage =
173 win_ver >= 0x602
174 ? " (Aero is always on for windows 8 and above)"
175 : "";
176
177 HMODULE dwm = LoadLibraryW(L"dwmapi");
178 BOOL bComposition = true;
179
180 if (!dwm) {
181 return;
182 }
183
184 composition_enabled = (dwm_is_composition_enabled_t)GetProcAddress(
185 dwm, "DwmIsCompositionEnabled");
186 if (!composition_enabled) {
187 FreeLibrary(dwm);
188 return;
189 }
190
191 composition_enabled(&bComposition);
192 blog(LOG_INFO, "Aero is %s%s", bComposition ? "Enabled" : "Disabled",
193 aeroMessage);
194 }
195
196 #define WIN10_GAME_BAR_REG_KEY \
197 L"Software\\Microsoft\\Windows\\CurrentVersion\\GameDVR"
198 #define WIN10_GAME_DVR_POLICY_REG_KEY \
199 L"SOFTWARE\\Policies\\Microsoft\\Windows\\GameDVR"
200 #define WIN10_GAME_DVR_REG_KEY L"System\\GameConfigStore"
201 #define WIN10_GAME_MODE_REG_KEY L"Software\\Microsoft\\GameBar"
202 #define WIN10_HAGS_REG_KEY \
203 L"SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers"
204
log_gaming_features(void)205 static void log_gaming_features(void)
206 {
207 if (win_ver < 0xA00)
208 return;
209
210 struct reg_dword game_bar_enabled;
211 struct reg_dword game_dvr_allowed;
212 struct reg_dword game_dvr_enabled;
213 struct reg_dword game_dvr_bg_recording;
214 struct reg_dword game_mode_enabled;
215 struct reg_dword hags_enabled;
216
217 get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_BAR_REG_KEY,
218 L"AppCaptureEnabled", &game_bar_enabled);
219 get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_DVR_POLICY_REG_KEY,
220 L"AllowGameDVR", &game_dvr_allowed);
221 get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_DVR_REG_KEY,
222 L"GameDVR_Enabled", &game_dvr_enabled);
223 get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_BAR_REG_KEY,
224 L"HistoricalCaptureEnabled", &game_dvr_bg_recording);
225 get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_MODE_REG_KEY,
226 L"AllowAutoGameMode", &game_mode_enabled);
227 get_reg_dword(HKEY_LOCAL_MACHINE, WIN10_HAGS_REG_KEY, L"HwSchMode",
228 &hags_enabled);
229
230 if (game_mode_enabled.status != ERROR_SUCCESS) {
231 get_reg_dword(HKEY_CURRENT_USER, WIN10_GAME_MODE_REG_KEY,
232 L"AutoGameModeEnabled", &game_mode_enabled);
233 }
234
235 blog(LOG_INFO, "Windows 10 Gaming Features:");
236 if (game_bar_enabled.status == ERROR_SUCCESS) {
237 blog(LOG_INFO, "\tGame Bar: %s",
238 (bool)game_bar_enabled.return_value ? "On" : "Off");
239 }
240
241 if (game_dvr_allowed.status == ERROR_SUCCESS) {
242 blog(LOG_INFO, "\tGame DVR Allowed: %s",
243 (bool)game_dvr_allowed.return_value ? "Yes" : "No");
244 }
245
246 if (game_dvr_enabled.status == ERROR_SUCCESS) {
247 blog(LOG_INFO, "\tGame DVR: %s",
248 (bool)game_dvr_enabled.return_value ? "On" : "Off");
249 }
250
251 if (game_dvr_bg_recording.status == ERROR_SUCCESS) {
252 blog(LOG_INFO, "\tGame DVR Background Recording: %s",
253 (bool)game_dvr_bg_recording.return_value ? "On" : "Off");
254 }
255
256 if (game_mode_enabled.status == ERROR_SUCCESS) {
257 blog(LOG_INFO, "\tGame Mode: %s",
258 (bool)game_mode_enabled.return_value ? "On" : "Off");
259 }
260
261 if (hags_enabled.status == ERROR_SUCCESS) {
262 blog(LOG_INFO, "\tHardware GPU Scheduler: %s",
263 (hags_enabled.return_value == 2) ? "On" : "Off");
264 }
265 }
266
get_str_for_state(int state)267 static const char *get_str_for_state(int state)
268 {
269 switch (state) {
270 case WSC_SECURITY_PRODUCT_STATE_ON:
271 return "enabled";
272 case WSC_SECURITY_PRODUCT_STATE_OFF:
273 return "disabled";
274 case WSC_SECURITY_PRODUCT_STATE_SNOOZED:
275 return "temporarily disabled";
276 case WSC_SECURITY_PRODUCT_STATE_EXPIRED:
277 return "expired";
278 default:
279 return "unknown";
280 }
281 }
282
get_str_for_type(int type)283 static const char *get_str_for_type(int type)
284 {
285 switch (type) {
286 case WSC_SECURITY_PROVIDER_ANTIVIRUS:
287 return "AV";
288 case WSC_SECURITY_PROVIDER_FIREWALL:
289 return "FW";
290 case WSC_SECURITY_PROVIDER_ANTISPYWARE:
291 return "ASW";
292 default:
293 return "unknown";
294 }
295 }
296
log_security_products_by_type(IWSCProductList * prod_list,int type)297 static void log_security_products_by_type(IWSCProductList *prod_list, int type)
298 {
299 HRESULT hr;
300 LONG count = 0;
301 IWscProduct *prod;
302 BSTR name;
303 WSC_SECURITY_PRODUCT_STATE prod_state;
304
305 hr = prod_list->lpVtbl->Initialize(prod_list, type);
306
307 if (FAILED(hr))
308 return;
309
310 hr = prod_list->lpVtbl->get_Count(prod_list, &count);
311 if (FAILED(hr)) {
312 prod_list->lpVtbl->Release(prod_list);
313 return;
314 }
315
316 for (int i = 0; i < count; i++) {
317 hr = prod_list->lpVtbl->get_Item(prod_list, i, &prod);
318 if (FAILED(hr))
319 continue;
320
321 hr = prod->lpVtbl->get_ProductName(prod, &name);
322 if (FAILED(hr))
323 continue;
324
325 hr = prod->lpVtbl->get_ProductState(prod, &prod_state);
326 if (FAILED(hr)) {
327 SysFreeString(name);
328 continue;
329 }
330
331 blog(LOG_INFO, "\t%S: %s (%s)", name,
332 get_str_for_state(prod_state), get_str_for_type(type));
333
334 SysFreeString(name);
335 prod->lpVtbl->Release(prod);
336 }
337
338 prod_list->lpVtbl->Release(prod_list);
339 }
340
log_security_products(void)341 static void log_security_products(void)
342 {
343 IWSCProductList *prod_list = NULL;
344 HMODULE h_wsc;
345 HRESULT hr;
346
347 /* We load the DLL rather than import wcsapi.lib because the clsid /
348 * iid only exists on Windows 8 or higher. */
349
350 h_wsc = LoadLibraryW(L"wscapi.dll");
351 if (!h_wsc)
352 return;
353
354 const CLSID *prod_list_clsid =
355 (const CLSID *)GetProcAddress(h_wsc, "CLSID_WSCProductList");
356 const IID *prod_list_iid =
357 (const IID *)GetProcAddress(h_wsc, "IID_IWSCProductList");
358
359 if (prod_list_clsid && prod_list_iid) {
360 blog(LOG_INFO, "Sec. Software Status:");
361
362 hr = CoCreateInstance(prod_list_clsid, NULL,
363 CLSCTX_INPROC_SERVER, prod_list_iid,
364 &prod_list);
365 if (!FAILED(hr)) {
366 log_security_products_by_type(
367 prod_list, WSC_SECURITY_PROVIDER_ANTIVIRUS);
368 }
369
370 hr = CoCreateInstance(prod_list_clsid, NULL,
371 CLSCTX_INPROC_SERVER, prod_list_iid,
372 &prod_list);
373 if (!FAILED(hr)) {
374 log_security_products_by_type(
375 prod_list, WSC_SECURITY_PROVIDER_FIREWALL);
376 }
377
378 hr = CoCreateInstance(prod_list_clsid, NULL,
379 CLSCTX_INPROC_SERVER, prod_list_iid,
380 &prod_list);
381 if (!FAILED(hr)) {
382 log_security_products_by_type(
383 prod_list, WSC_SECURITY_PROVIDER_ANTISPYWARE);
384 }
385 }
386
387 FreeLibrary(h_wsc);
388 }
389
log_system_info(void)390 void log_system_info(void)
391 {
392 struct win_version_info ver;
393 get_win_ver(&ver);
394
395 win_ver = (ver.major << 8) | ver.minor;
396
397 log_processor_info();
398 log_processor_cores();
399 log_available_memory();
400 log_windows_version();
401 log_admin_status();
402 log_aero();
403 log_gaming_features();
404 log_security_products();
405 }
406
407 struct obs_hotkeys_platform {
408 int vk_codes[OBS_KEY_LAST_VALUE];
409 };
410
get_virtual_key(obs_key_t key)411 static int get_virtual_key(obs_key_t key)
412 {
413 switch (key) {
414 case OBS_KEY_RETURN:
415 return VK_RETURN;
416 case OBS_KEY_ESCAPE:
417 return VK_ESCAPE;
418 case OBS_KEY_TAB:
419 return VK_TAB;
420 case OBS_KEY_BACKTAB:
421 return VK_OEM_BACKTAB;
422 case OBS_KEY_BACKSPACE:
423 return VK_BACK;
424 case OBS_KEY_INSERT:
425 return VK_INSERT;
426 case OBS_KEY_DELETE:
427 return VK_DELETE;
428 case OBS_KEY_PAUSE:
429 return VK_PAUSE;
430 case OBS_KEY_PRINT:
431 return VK_SNAPSHOT;
432 case OBS_KEY_CLEAR:
433 return VK_CLEAR;
434 case OBS_KEY_HOME:
435 return VK_HOME;
436 case OBS_KEY_END:
437 return VK_END;
438 case OBS_KEY_LEFT:
439 return VK_LEFT;
440 case OBS_KEY_UP:
441 return VK_UP;
442 case OBS_KEY_RIGHT:
443 return VK_RIGHT;
444 case OBS_KEY_DOWN:
445 return VK_DOWN;
446 case OBS_KEY_PAGEUP:
447 return VK_PRIOR;
448 case OBS_KEY_PAGEDOWN:
449 return VK_NEXT;
450
451 case OBS_KEY_SHIFT:
452 return VK_SHIFT;
453 case OBS_KEY_CONTROL:
454 return VK_CONTROL;
455 case OBS_KEY_ALT:
456 return VK_MENU;
457 case OBS_KEY_CAPSLOCK:
458 return VK_CAPITAL;
459 case OBS_KEY_NUMLOCK:
460 return VK_NUMLOCK;
461 case OBS_KEY_SCROLLLOCK:
462 return VK_SCROLL;
463
464 case OBS_KEY_F1:
465 return VK_F1;
466 case OBS_KEY_F2:
467 return VK_F2;
468 case OBS_KEY_F3:
469 return VK_F3;
470 case OBS_KEY_F4:
471 return VK_F4;
472 case OBS_KEY_F5:
473 return VK_F5;
474 case OBS_KEY_F6:
475 return VK_F6;
476 case OBS_KEY_F7:
477 return VK_F7;
478 case OBS_KEY_F8:
479 return VK_F8;
480 case OBS_KEY_F9:
481 return VK_F9;
482 case OBS_KEY_F10:
483 return VK_F10;
484 case OBS_KEY_F11:
485 return VK_F11;
486 case OBS_KEY_F12:
487 return VK_F12;
488 case OBS_KEY_F13:
489 return VK_F13;
490 case OBS_KEY_F14:
491 return VK_F14;
492 case OBS_KEY_F15:
493 return VK_F15;
494 case OBS_KEY_F16:
495 return VK_F16;
496 case OBS_KEY_F17:
497 return VK_F17;
498 case OBS_KEY_F18:
499 return VK_F18;
500 case OBS_KEY_F19:
501 return VK_F19;
502 case OBS_KEY_F20:
503 return VK_F20;
504 case OBS_KEY_F21:
505 return VK_F21;
506 case OBS_KEY_F22:
507 return VK_F22;
508 case OBS_KEY_F23:
509 return VK_F23;
510 case OBS_KEY_F24:
511 return VK_F24;
512
513 case OBS_KEY_SPACE:
514 return VK_SPACE;
515
516 case OBS_KEY_APOSTROPHE:
517 return VK_OEM_7;
518 case OBS_KEY_PLUS:
519 return VK_OEM_PLUS;
520 case OBS_KEY_COMMA:
521 return VK_OEM_COMMA;
522 case OBS_KEY_MINUS:
523 return VK_OEM_MINUS;
524 case OBS_KEY_PERIOD:
525 return VK_OEM_PERIOD;
526 case OBS_KEY_SLASH:
527 return VK_OEM_2;
528 case OBS_KEY_0:
529 return '0';
530 case OBS_KEY_1:
531 return '1';
532 case OBS_KEY_2:
533 return '2';
534 case OBS_KEY_3:
535 return '3';
536 case OBS_KEY_4:
537 return '4';
538 case OBS_KEY_5:
539 return '5';
540 case OBS_KEY_6:
541 return '6';
542 case OBS_KEY_7:
543 return '7';
544 case OBS_KEY_8:
545 return '8';
546 case OBS_KEY_9:
547 return '9';
548 case OBS_KEY_NUMASTERISK:
549 return VK_MULTIPLY;
550 case OBS_KEY_NUMPLUS:
551 return VK_ADD;
552 case OBS_KEY_NUMMINUS:
553 return VK_SUBTRACT;
554 case OBS_KEY_NUMPERIOD:
555 return VK_DECIMAL;
556 case OBS_KEY_NUMSLASH:
557 return VK_DIVIDE;
558 case OBS_KEY_NUM0:
559 return VK_NUMPAD0;
560 case OBS_KEY_NUM1:
561 return VK_NUMPAD1;
562 case OBS_KEY_NUM2:
563 return VK_NUMPAD2;
564 case OBS_KEY_NUM3:
565 return VK_NUMPAD3;
566 case OBS_KEY_NUM4:
567 return VK_NUMPAD4;
568 case OBS_KEY_NUM5:
569 return VK_NUMPAD5;
570 case OBS_KEY_NUM6:
571 return VK_NUMPAD6;
572 case OBS_KEY_NUM7:
573 return VK_NUMPAD7;
574 case OBS_KEY_NUM8:
575 return VK_NUMPAD8;
576 case OBS_KEY_NUM9:
577 return VK_NUMPAD9;
578 case OBS_KEY_SEMICOLON:
579 return VK_OEM_1;
580 case OBS_KEY_A:
581 return 'A';
582 case OBS_KEY_B:
583 return 'B';
584 case OBS_KEY_C:
585 return 'C';
586 case OBS_KEY_D:
587 return 'D';
588 case OBS_KEY_E:
589 return 'E';
590 case OBS_KEY_F:
591 return 'F';
592 case OBS_KEY_G:
593 return 'G';
594 case OBS_KEY_H:
595 return 'H';
596 case OBS_KEY_I:
597 return 'I';
598 case OBS_KEY_J:
599 return 'J';
600 case OBS_KEY_K:
601 return 'K';
602 case OBS_KEY_L:
603 return 'L';
604 case OBS_KEY_M:
605 return 'M';
606 case OBS_KEY_N:
607 return 'N';
608 case OBS_KEY_O:
609 return 'O';
610 case OBS_KEY_P:
611 return 'P';
612 case OBS_KEY_Q:
613 return 'Q';
614 case OBS_KEY_R:
615 return 'R';
616 case OBS_KEY_S:
617 return 'S';
618 case OBS_KEY_T:
619 return 'T';
620 case OBS_KEY_U:
621 return 'U';
622 case OBS_KEY_V:
623 return 'V';
624 case OBS_KEY_W:
625 return 'W';
626 case OBS_KEY_X:
627 return 'X';
628 case OBS_KEY_Y:
629 return 'Y';
630 case OBS_KEY_Z:
631 return 'Z';
632 case OBS_KEY_BRACKETLEFT:
633 return VK_OEM_4;
634 case OBS_KEY_BACKSLASH:
635 return VK_OEM_5;
636 case OBS_KEY_BRACKETRIGHT:
637 return VK_OEM_6;
638 case OBS_KEY_ASCIITILDE:
639 return VK_OEM_3;
640
641 case OBS_KEY_HENKAN:
642 return VK_CONVERT;
643 case OBS_KEY_MUHENKAN:
644 return VK_NONCONVERT;
645 case OBS_KEY_KANJI:
646 return VK_KANJI;
647 case OBS_KEY_TOUROKU:
648 return VK_OEM_FJ_TOUROKU;
649 case OBS_KEY_MASSYO:
650 return VK_OEM_FJ_MASSHOU;
651
652 case OBS_KEY_HANGUL:
653 return VK_HANGUL;
654
655 case OBS_KEY_BACKSLASH_RT102:
656 return VK_OEM_102;
657
658 case OBS_KEY_MOUSE1:
659 return VK_LBUTTON;
660 case OBS_KEY_MOUSE2:
661 return VK_RBUTTON;
662 case OBS_KEY_MOUSE3:
663 return VK_MBUTTON;
664 case OBS_KEY_MOUSE4:
665 return VK_XBUTTON1;
666 case OBS_KEY_MOUSE5:
667 return VK_XBUTTON2;
668
669 case OBS_KEY_VK_CANCEL:
670 return VK_CANCEL;
671 case OBS_KEY_0x07:
672 return 0x07;
673 case OBS_KEY_0x0A:
674 return 0x0A;
675 case OBS_KEY_0x0B:
676 return 0x0B;
677 case OBS_KEY_0x0E:
678 return 0x0E;
679 case OBS_KEY_0x0F:
680 return 0x0F;
681 case OBS_KEY_0x16:
682 return 0x16;
683 case OBS_KEY_VK_JUNJA:
684 return VK_JUNJA;
685 case OBS_KEY_VK_FINAL:
686 return VK_FINAL;
687 case OBS_KEY_0x1A:
688 return 0x1A;
689 case OBS_KEY_VK_ACCEPT:
690 return VK_ACCEPT;
691 case OBS_KEY_VK_MODECHANGE:
692 return VK_MODECHANGE;
693 case OBS_KEY_VK_SELECT:
694 return VK_SELECT;
695 case OBS_KEY_VK_PRINT:
696 return VK_PRINT;
697 case OBS_KEY_VK_EXECUTE:
698 return VK_EXECUTE;
699 case OBS_KEY_VK_HELP:
700 return VK_HELP;
701 case OBS_KEY_0x30:
702 return 0x30;
703 case OBS_KEY_0x31:
704 return 0x31;
705 case OBS_KEY_0x32:
706 return 0x32;
707 case OBS_KEY_0x33:
708 return 0x33;
709 case OBS_KEY_0x34:
710 return 0x34;
711 case OBS_KEY_0x35:
712 return 0x35;
713 case OBS_KEY_0x36:
714 return 0x36;
715 case OBS_KEY_0x37:
716 return 0x37;
717 case OBS_KEY_0x38:
718 return 0x38;
719 case OBS_KEY_0x39:
720 return 0x39;
721 case OBS_KEY_0x3A:
722 return 0x3A;
723 case OBS_KEY_0x3B:
724 return 0x3B;
725 case OBS_KEY_0x3C:
726 return 0x3C;
727 case OBS_KEY_0x3D:
728 return 0x3D;
729 case OBS_KEY_0x3E:
730 return 0x3E;
731 case OBS_KEY_0x3F:
732 return 0x3F;
733 case OBS_KEY_0x40:
734 return 0x40;
735 case OBS_KEY_0x41:
736 return 0x41;
737 case OBS_KEY_0x42:
738 return 0x42;
739 case OBS_KEY_0x43:
740 return 0x43;
741 case OBS_KEY_0x44:
742 return 0x44;
743 case OBS_KEY_0x45:
744 return 0x45;
745 case OBS_KEY_0x46:
746 return 0x46;
747 case OBS_KEY_0x47:
748 return 0x47;
749 case OBS_KEY_0x48:
750 return 0x48;
751 case OBS_KEY_0x49:
752 return 0x49;
753 case OBS_KEY_0x4A:
754 return 0x4A;
755 case OBS_KEY_0x4B:
756 return 0x4B;
757 case OBS_KEY_0x4C:
758 return 0x4C;
759 case OBS_KEY_0x4D:
760 return 0x4D;
761 case OBS_KEY_0x4E:
762 return 0x4E;
763 case OBS_KEY_0x4F:
764 return 0x4F;
765 case OBS_KEY_0x50:
766 return 0x50;
767 case OBS_KEY_0x51:
768 return 0x51;
769 case OBS_KEY_0x52:
770 return 0x52;
771 case OBS_KEY_0x53:
772 return 0x53;
773 case OBS_KEY_0x54:
774 return 0x54;
775 case OBS_KEY_0x55:
776 return 0x55;
777 case OBS_KEY_0x56:
778 return 0x56;
779 case OBS_KEY_0x57:
780 return 0x57;
781 case OBS_KEY_0x58:
782 return 0x58;
783 case OBS_KEY_0x59:
784 return 0x59;
785 case OBS_KEY_0x5A:
786 return 0x5A;
787 case OBS_KEY_VK_LWIN:
788 return VK_LWIN;
789 case OBS_KEY_VK_RWIN:
790 return VK_RWIN;
791 case OBS_KEY_VK_APPS:
792 return VK_APPS;
793 case OBS_KEY_0x5E:
794 return 0x5E;
795 case OBS_KEY_VK_SLEEP:
796 return VK_SLEEP;
797 case OBS_KEY_VK_SEPARATOR:
798 return VK_SEPARATOR;
799 case OBS_KEY_0x88:
800 return 0x88;
801 case OBS_KEY_0x89:
802 return 0x89;
803 case OBS_KEY_0x8A:
804 return 0x8A;
805 case OBS_KEY_0x8B:
806 return 0x8B;
807 case OBS_KEY_0x8C:
808 return 0x8C;
809 case OBS_KEY_0x8D:
810 return 0x8D;
811 case OBS_KEY_0x8E:
812 return 0x8E;
813 case OBS_KEY_0x8F:
814 return 0x8F;
815 case OBS_KEY_VK_OEM_FJ_JISHO:
816 return VK_OEM_FJ_JISHO;
817 case OBS_KEY_VK_OEM_FJ_LOYA:
818 return VK_OEM_FJ_LOYA;
819 case OBS_KEY_VK_OEM_FJ_ROYA:
820 return VK_OEM_FJ_ROYA;
821 case OBS_KEY_0x97:
822 return 0x97;
823 case OBS_KEY_0x98:
824 return 0x98;
825 case OBS_KEY_0x99:
826 return 0x99;
827 case OBS_KEY_0x9A:
828 return 0x9A;
829 case OBS_KEY_0x9B:
830 return 0x9B;
831 case OBS_KEY_0x9C:
832 return 0x9C;
833 case OBS_KEY_0x9D:
834 return 0x9D;
835 case OBS_KEY_0x9E:
836 return 0x9E;
837 case OBS_KEY_0x9F:
838 return 0x9F;
839 case OBS_KEY_VK_LSHIFT:
840 return VK_LSHIFT;
841 case OBS_KEY_VK_RSHIFT:
842 return VK_RSHIFT;
843 case OBS_KEY_VK_LCONTROL:
844 return VK_LCONTROL;
845 case OBS_KEY_VK_RCONTROL:
846 return VK_RCONTROL;
847 case OBS_KEY_VK_LMENU:
848 return VK_LMENU;
849 case OBS_KEY_VK_RMENU:
850 return VK_RMENU;
851 case OBS_KEY_VK_BROWSER_BACK:
852 return VK_BROWSER_BACK;
853 case OBS_KEY_VK_BROWSER_FORWARD:
854 return VK_BROWSER_FORWARD;
855 case OBS_KEY_VK_BROWSER_REFRESH:
856 return VK_BROWSER_REFRESH;
857 case OBS_KEY_VK_BROWSER_STOP:
858 return VK_BROWSER_STOP;
859 case OBS_KEY_VK_BROWSER_SEARCH:
860 return VK_BROWSER_SEARCH;
861 case OBS_KEY_VK_BROWSER_FAVORITES:
862 return VK_BROWSER_FAVORITES;
863 case OBS_KEY_VK_BROWSER_HOME:
864 return VK_BROWSER_HOME;
865 case OBS_KEY_VK_VOLUME_MUTE:
866 return VK_VOLUME_MUTE;
867 case OBS_KEY_VK_VOLUME_DOWN:
868 return VK_VOLUME_DOWN;
869 case OBS_KEY_VK_VOLUME_UP:
870 return VK_VOLUME_UP;
871 case OBS_KEY_VK_MEDIA_NEXT_TRACK:
872 return VK_MEDIA_NEXT_TRACK;
873 case OBS_KEY_VK_MEDIA_PREV_TRACK:
874 return VK_MEDIA_PREV_TRACK;
875 case OBS_KEY_VK_MEDIA_STOP:
876 return VK_MEDIA_STOP;
877 case OBS_KEY_VK_MEDIA_PLAY_PAUSE:
878 return VK_MEDIA_PLAY_PAUSE;
879 case OBS_KEY_VK_LAUNCH_MAIL:
880 return VK_LAUNCH_MAIL;
881 case OBS_KEY_VK_LAUNCH_MEDIA_SELECT:
882 return VK_LAUNCH_MEDIA_SELECT;
883 case OBS_KEY_VK_LAUNCH_APP1:
884 return VK_LAUNCH_APP1;
885 case OBS_KEY_VK_LAUNCH_APP2:
886 return VK_LAUNCH_APP2;
887 case OBS_KEY_0xB8:
888 return 0xB8;
889 case OBS_KEY_0xB9:
890 return 0xB9;
891 case OBS_KEY_0xC1:
892 return 0xC1;
893 case OBS_KEY_0xC2:
894 return 0xC2;
895 case OBS_KEY_0xC3:
896 return 0xC3;
897 case OBS_KEY_0xC4:
898 return 0xC4;
899 case OBS_KEY_0xC5:
900 return 0xC5;
901 case OBS_KEY_0xC6:
902 return 0xC6;
903 case OBS_KEY_0xC7:
904 return 0xC7;
905 case OBS_KEY_0xC8:
906 return 0xC8;
907 case OBS_KEY_0xC9:
908 return 0xC9;
909 case OBS_KEY_0xCA:
910 return 0xCA;
911 case OBS_KEY_0xCB:
912 return 0xCB;
913 case OBS_KEY_0xCC:
914 return 0xCC;
915 case OBS_KEY_0xCD:
916 return 0xCD;
917 case OBS_KEY_0xCE:
918 return 0xCE;
919 case OBS_KEY_0xCF:
920 return 0xCF;
921 case OBS_KEY_0xD0:
922 return 0xD0;
923 case OBS_KEY_0xD1:
924 return 0xD1;
925 case OBS_KEY_0xD2:
926 return 0xD2;
927 case OBS_KEY_0xD3:
928 return 0xD3;
929 case OBS_KEY_0xD4:
930 return 0xD4;
931 case OBS_KEY_0xD5:
932 return 0xD5;
933 case OBS_KEY_0xD6:
934 return 0xD6;
935 case OBS_KEY_0xD7:
936 return 0xD7;
937 case OBS_KEY_0xD8:
938 return 0xD8;
939 case OBS_KEY_0xD9:
940 return 0xD9;
941 case OBS_KEY_0xDA:
942 return 0xDA;
943 case OBS_KEY_VK_OEM_8:
944 return VK_OEM_8;
945 case OBS_KEY_0xE0:
946 return 0xE0;
947 case OBS_KEY_VK_OEM_AX:
948 return VK_OEM_AX;
949 case OBS_KEY_VK_ICO_HELP:
950 return VK_ICO_HELP;
951 case OBS_KEY_VK_ICO_00:
952 return VK_ICO_00;
953 case OBS_KEY_VK_PROCESSKEY:
954 return VK_PROCESSKEY;
955 case OBS_KEY_VK_ICO_CLEAR:
956 return VK_ICO_CLEAR;
957 case OBS_KEY_VK_PACKET:
958 return VK_PACKET;
959 case OBS_KEY_0xE8:
960 return 0xE8;
961 case OBS_KEY_VK_OEM_RESET:
962 return VK_OEM_RESET;
963 case OBS_KEY_VK_OEM_JUMP:
964 return VK_OEM_JUMP;
965 case OBS_KEY_VK_OEM_PA1:
966 return VK_OEM_PA1;
967 case OBS_KEY_VK_OEM_PA2:
968 return VK_OEM_PA2;
969 case OBS_KEY_VK_OEM_PA3:
970 return VK_OEM_PA3;
971 case OBS_KEY_VK_OEM_WSCTRL:
972 return VK_OEM_WSCTRL;
973 case OBS_KEY_VK_OEM_CUSEL:
974 return VK_OEM_CUSEL;
975 case OBS_KEY_VK_OEM_ATTN:
976 return VK_OEM_ATTN;
977 case OBS_KEY_VK_OEM_FINISH:
978 return VK_OEM_FINISH;
979 case OBS_KEY_VK_OEM_COPY:
980 return VK_OEM_COPY;
981 case OBS_KEY_VK_OEM_AUTO:
982 return VK_OEM_AUTO;
983 case OBS_KEY_VK_OEM_ENLW:
984 return VK_OEM_ENLW;
985 case OBS_KEY_VK_ATTN:
986 return VK_ATTN;
987 case OBS_KEY_VK_CRSEL:
988 return VK_CRSEL;
989 case OBS_KEY_VK_EXSEL:
990 return VK_EXSEL;
991 case OBS_KEY_VK_EREOF:
992 return VK_EREOF;
993 case OBS_KEY_VK_PLAY:
994 return VK_PLAY;
995 case OBS_KEY_VK_ZOOM:
996 return VK_ZOOM;
997 case OBS_KEY_VK_NONAME:
998 return VK_NONAME;
999 case OBS_KEY_VK_PA1:
1000 return VK_PA1;
1001 case OBS_KEY_VK_OEM_CLEAR:
1002 return VK_OEM_CLEAR;
1003
1004 /* TODO: Implement keys for non-US keyboards */
1005 default:;
1006 }
1007 return 0;
1008 }
1009
obs_hotkeys_platform_init(struct obs_core_hotkeys * hotkeys)1010 bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)
1011 {
1012 hotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t));
1013
1014 for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)
1015 hotkeys->platform_context->vk_codes[i] = get_virtual_key(i);
1016
1017 return true;
1018 }
1019
obs_hotkeys_platform_free(struct obs_core_hotkeys * hotkeys)1020 void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)
1021 {
1022 bfree(hotkeys->platform_context);
1023 hotkeys->platform_context = NULL;
1024 }
1025
vk_down(DWORD vk)1026 static bool vk_down(DWORD vk)
1027 {
1028 short state = GetAsyncKeyState(vk);
1029 bool down = (state & 0x8000) != 0;
1030
1031 return down;
1032 }
1033
obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t * context,obs_key_t key)1034 bool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context,
1035 obs_key_t key)
1036 {
1037 if (key == OBS_KEY_META) {
1038 return vk_down(VK_LWIN) || vk_down(VK_RWIN);
1039 }
1040
1041 UNUSED_PARAMETER(context);
1042 return vk_down(obs_key_to_virtual_key(key));
1043 }
1044
obs_key_to_str(obs_key_t key,struct dstr * str)1045 void obs_key_to_str(obs_key_t key, struct dstr *str)
1046 {
1047 wchar_t name[128] = L"";
1048 UINT scan_code;
1049 int vk;
1050
1051 if (key == OBS_KEY_NONE) {
1052 return;
1053
1054 } else if (key >= OBS_KEY_F13 && key <= OBS_KEY_F24) {
1055 dstr_printf(str, "F%d", (int)(key - OBS_KEY_F13 + 13));
1056 return;
1057 } else if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {
1058 if (obs->hotkeys.translations[key]) {
1059 dstr_copy(str, obs->hotkeys.translations[key]);
1060 } else {
1061 dstr_printf(str, "Mouse %d",
1062 (int)(key - OBS_KEY_MOUSE1 + 1));
1063 }
1064 return;
1065 }
1066 if (key == OBS_KEY_PAUSE) {
1067 dstr_copy(str, obs_get_hotkey_translation(key, "Pause"));
1068 return;
1069
1070 } else if (key == OBS_KEY_META) {
1071 dstr_copy(str, obs_get_hotkey_translation(key, "Windows"));
1072 return;
1073 }
1074
1075 vk = obs_key_to_virtual_key(key);
1076 scan_code = MapVirtualKey(vk, 0) << 16;
1077
1078 switch (vk) {
1079 case VK_HOME:
1080 case VK_END:
1081 case VK_LEFT:
1082 case VK_UP:
1083 case VK_RIGHT:
1084 case VK_DOWN:
1085 case VK_PRIOR:
1086 case VK_NEXT:
1087 case VK_INSERT:
1088 case VK_DELETE:
1089 case VK_NUMLOCK:
1090 scan_code |= 0x01000000;
1091 }
1092
1093 if ((key < OBS_KEY_VK_CANCEL || key > OBS_KEY_VK_OEM_CLEAR) &&
1094 scan_code != 0 && GetKeyNameTextW(scan_code, name, 128) != 0) {
1095 dstr_from_wcs(str, name);
1096 } else if (key != OBS_KEY_NONE) {
1097 dstr_copy(str, obs_key_to_name(key));
1098 }
1099 }
1100
obs_key_from_virtual_key(int code)1101 obs_key_t obs_key_from_virtual_key(int code)
1102 {
1103 obs_hotkeys_platform_t *platform = obs->hotkeys.platform_context;
1104
1105 for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {
1106 if (platform->vk_codes[i] == code) {
1107 return (obs_key_t)i;
1108 }
1109 }
1110
1111 return OBS_KEY_NONE;
1112 }
1113
obs_key_to_virtual_key(obs_key_t key)1114 int obs_key_to_virtual_key(obs_key_t key)
1115 {
1116 if (key == OBS_KEY_META)
1117 return VK_LWIN;
1118
1119 return obs->hotkeys.platform_context->vk_codes[(int)key];
1120 }
1121
add_combo_key(obs_key_t key,struct dstr * str)1122 static inline void add_combo_key(obs_key_t key, struct dstr *str)
1123 {
1124 struct dstr key_str = {0};
1125
1126 obs_key_to_str(key, &key_str);
1127
1128 if (!dstr_is_empty(&key_str)) {
1129 if (!dstr_is_empty(str)) {
1130 dstr_cat(str, " + ");
1131 }
1132 dstr_cat_dstr(str, &key_str);
1133 }
1134
1135 dstr_free(&key_str);
1136 }
1137
obs_key_combination_to_str(obs_key_combination_t combination,struct dstr * str)1138 void obs_key_combination_to_str(obs_key_combination_t combination,
1139 struct dstr *str)
1140 {
1141 if ((combination.modifiers & INTERACT_CONTROL_KEY) != 0) {
1142 add_combo_key(OBS_KEY_CONTROL, str);
1143 }
1144 if ((combination.modifiers & INTERACT_COMMAND_KEY) != 0) {
1145 add_combo_key(OBS_KEY_META, str);
1146 }
1147 if ((combination.modifiers & INTERACT_ALT_KEY) != 0) {
1148 add_combo_key(OBS_KEY_ALT, str);
1149 }
1150 if ((combination.modifiers & INTERACT_SHIFT_KEY) != 0) {
1151 add_combo_key(OBS_KEY_SHIFT, str);
1152 }
1153 if (combination.key != OBS_KEY_NONE) {
1154 add_combo_key(combination.key, str);
1155 }
1156 }
1157
1158 bool sym_initialize_called = false;
1159
reset_win32_symbol_paths(void)1160 void reset_win32_symbol_paths(void)
1161 {
1162 static BOOL(WINAPI * sym_initialize_w)(HANDLE, const wchar_t *, BOOL);
1163 static BOOL(WINAPI * sym_set_search_path_w)(HANDLE, const wchar_t *);
1164 static bool funcs_initialized = false;
1165 static bool initialize_success = false;
1166
1167 struct obs_module *module = obs->first_module;
1168 struct dstr path_str = {0};
1169 DARRAY(char *) paths;
1170 wchar_t *path_str_w = NULL;
1171 char *abspath;
1172
1173 da_init(paths);
1174
1175 if (!funcs_initialized) {
1176 HMODULE mod;
1177 funcs_initialized = true;
1178
1179 mod = LoadLibraryW(L"DbgHelp");
1180 if (!mod)
1181 return;
1182
1183 sym_initialize_w =
1184 (void *)GetProcAddress(mod, "SymInitializeW");
1185 sym_set_search_path_w =
1186 (void *)GetProcAddress(mod, "SymSetSearchPathW");
1187 if (!sym_initialize_w || !sym_set_search_path_w) {
1188 FreeLibrary(mod);
1189 return;
1190 }
1191
1192 initialize_success = true;
1193 // Leaks 'mod' once.
1194 }
1195
1196 if (!initialize_success)
1197 return;
1198
1199 abspath = os_get_abs_path_ptr(".");
1200 if (abspath)
1201 da_push_back(paths, &abspath);
1202
1203 while (module) {
1204 bool found = false;
1205 struct dstr path = {0};
1206 char *path_end;
1207
1208 dstr_copy(&path, module->bin_path);
1209 dstr_replace(&path, "/", "\\");
1210
1211 path_end = strrchr(path.array, '\\');
1212 if (!path_end) {
1213 module = module->next;
1214 dstr_free(&path);
1215 continue;
1216 }
1217
1218 *path_end = 0;
1219
1220 for (size_t i = 0; i < paths.num; i++) {
1221 const char *existing_path = paths.array[i];
1222 if (astrcmpi(path.array, existing_path) == 0) {
1223 found = true;
1224 break;
1225 }
1226 }
1227
1228 if (!found) {
1229 abspath = os_get_abs_path_ptr(path.array);
1230 if (abspath)
1231 da_push_back(paths, &abspath);
1232 }
1233
1234 dstr_free(&path);
1235
1236 module = module->next;
1237 }
1238
1239 for (size_t i = 0; i < paths.num; i++) {
1240 const char *path = paths.array[i];
1241 if (path && *path) {
1242 if (i != 0)
1243 dstr_cat(&path_str, ";");
1244 dstr_cat(&path_str, paths.array[i]);
1245 }
1246 }
1247
1248 if (path_str.array) {
1249 os_utf8_to_wcs_ptr(path_str.array, path_str.len, &path_str_w);
1250 if (path_str_w) {
1251 if (!sym_initialize_called) {
1252 sym_initialize_w(GetCurrentProcess(),
1253 path_str_w, false);
1254 sym_initialize_called = true;
1255 } else {
1256 sym_set_search_path_w(GetCurrentProcess(),
1257 path_str_w);
1258 }
1259
1260 bfree(path_str_w);
1261 }
1262 }
1263
1264 for (size_t i = 0; i < paths.num; i++)
1265 bfree(paths.array[i]);
1266 dstr_free(&path_str);
1267 da_free(paths);
1268 }
1269
1270 extern void initialize_crash_handler(void);
1271
obs_init_win32_crash_handler(void)1272 void obs_init_win32_crash_handler(void)
1273 {
1274 initialize_crash_handler();
1275 }
1276
initialize_com(void)1277 bool initialize_com(void)
1278 {
1279 const HRESULT hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
1280 const bool success = SUCCEEDED(hr);
1281 if (!success)
1282 blog(LOG_ERROR, "CoInitializeEx failed: 0x%08X", hr);
1283 return success;
1284 }
1285
uninitialize_com(void)1286 void uninitialize_com(void)
1287 {
1288 CoUninitialize();
1289 }
1290