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