1 // Windows layer-independent code
2 // (c) EDuke32 developers and contributors. All rights reserved. ;)
3
4 #include "winbits.h"
5
6 #include "baselayer.h"
7 #include "build.h"
8 #include "cache1d.h"
9 #include "compat.h"
10 #include "osd.h"
11 #include "renderlayer.h"
12
13 #include <mmsystem.h>
14 #include <winnls.h>
15 #include <winternl.h>
16
17 #include <system_error>
18
19 #ifdef BITNESS64
20 # define EBACKTRACEDLL "ebacktrace1-64.dll"
21 #else
22 # define EBACKTRACEDLL "ebacktrace1.dll"
23 #endif
24
25 int32_t win_priorityclass;
26 char win_silentvideomodeswitch;
27 static int win_silentfocuschange;
28
29 static HANDLE g_singleInstanceSemaphore = nullptr;
30 static int32_t win_togglecomposition;
31 static int32_t win_systemtimermode;
32 static int32_t win_performancemode;
33
34 static OSVERSIONINFOEX osv;
35 static FARPROC ntdll_wine_get_version;
36 static char const *enUSLayoutString = "00000409";
37
38 DWM_TIMING_INFO timingInfo;
39
40 static HMODULE hPOWRPROF;
41 static GUID *systemPowerSchemeGUID;
42
43 typedef DWORD(WINAPI *PFNPOWERGETACTIVESCHEME)(HKEY, GUID **);
44 typedef DWORD(WINAPI *PFNPOWERSETACTIVESCHEME)(HKEY, CONST GUID *);
45
46 static PFNPOWERGETACTIVESCHEME powrprof_PowerGetActiveScheme;
47 static PFNPOWERSETACTIVESCHEME powrprof_PowerSetActiveScheme;
48
windowsSetupTimer(int const useNtTimer)49 void windowsSetupTimer(int const useNtTimer)
50 {
51 if (ntdll_wine_get_version)
52 return;
53
54 typedef HRESULT(NTAPI* PFNSETTIMERRESOLUTION)(ULONG, BOOLEAN, PULONG);
55 typedef HRESULT(NTAPI* PFNQUERYTIMERRESOLUTION)(PULONG, PULONG, PULONG);
56
57 TIMECAPS timeCaps;
58
59 if (timeGetDevCaps(&timeCaps, sizeof(TIMECAPS)) == MMSYSERR_NOERROR)
60 {
61 #if defined RENDERTYPESDL && SDL_MAJOR_VERSION >= 2
62 int const onBattery = (SDL_GetPowerInfo(NULL, NULL) == SDL_POWERSTATE_ON_BATTERY);
63 #else
64 static constexpr int const onBattery = 0;
65 #endif
66 static int timePeriod;
67 static ULONG ntTimerRes;
68 static HMODULE hNTDLL = GetModuleHandle("ntdll.dll");
69
70 static PFNQUERYTIMERRESOLUTION ntdll_NtQueryTimerResolution;
71 static PFNSETTIMERRESOLUTION ntdll_NtSetTimerResolution;
72
73 if (useNtTimer)
74 {
75 if (!onBattery)
76 {
77 ntdll_NtQueryTimerResolution = (PFNQUERYTIMERRESOLUTION) (void(*))GetProcAddress(hNTDLL, "NtQueryTimerResolution");
78 ntdll_NtSetTimerResolution = (PFNSETTIMERRESOLUTION) (void(*))GetProcAddress(hNTDLL, "NtSetTimerResolution");
79
80 if (ntdll_NtQueryTimerResolution == nullptr || ntdll_NtSetTimerResolution == nullptr)
81 {
82 OSD_Printf("ERROR: unable to locate NtQueryTimerResolution or NtSetTimerResolution symbols in ntdll.dll!\n");
83 goto failsafe;
84 }
85
86 ULONG minRes, maxRes, actualRes;
87
88 ntdll_NtQueryTimerResolution(&minRes, &maxRes, &actualRes);
89
90 if (ntTimerRes != 0)
91 {
92 if (ntTimerRes == actualRes)
93 return;
94
95 ntdll_NtSetTimerResolution(actualRes, FALSE, &actualRes);
96 }
97
98 ntdll_NtSetTimerResolution(maxRes, TRUE, &actualRes);
99
100 ntTimerRes = actualRes;
101 timePeriod = 0;
102
103 if (!win_silentfocuschange)
104 OSD_Printf("Initialized %.1fms system timer\n", actualRes / 10000.0);
105
106 return;
107 }
108 else if (!win_silentfocuschange)
109 OSD_Printf("Low-latency timer mode not supported on battery power!\n");
110 }
111 else if (ntTimerRes != 0)
112 {
113 ntdll_NtSetTimerResolution(ntTimerRes, FALSE, &ntTimerRes);
114 ntTimerRes = 0;
115 }
116
117 failsafe:
118 int const newPeriod = min(max(timeCaps.wPeriodMin, 1u << onBattery), timeCaps.wPeriodMax);
119
120 if (timePeriod != 0)
121 {
122 if (timePeriod == newPeriod)
123 return;
124
125 timeEndPeriod(timePeriod);
126 }
127
128 timeBeginPeriod(newPeriod);
129
130 timePeriod = newPeriod;
131 ntTimerRes = 0;
132
133 if (!win_silentfocuschange)
134 OSD_Printf("Initialized %ums system timer\n", newPeriod);
135
136 return;
137 }
138
139 OSD_Printf("ERROR: unable to configure system timer!\n");
140 }
141
142 //
143 // CheckWinVersion() -- check to see what version of Windows we happen to be running under
144 //
windowsGetVersion(void)145 BOOL windowsGetVersion(void)
146 {
147 HMODULE hNTDLL = GetModuleHandle("ntdll.dll");
148
149 if (hNTDLL)
150 ntdll_wine_get_version = GetProcAddress(hNTDLL, "wine_get_version");
151
152 ZeroMemory(&osv, sizeof(osv));
153 osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
154
155 if (GetVersionEx((LPOSVERSIONINFOA)&osv)) return TRUE;
156
157 osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
158
159 if (GetVersionEx((LPOSVERSIONINFOA)&osv)) return TRUE;
160
161 return FALSE;
162 }
163
windowsPrintVersion(void)164 static void windowsPrintVersion(void)
165 {
166 const char *ver = "";
167
168 switch (osv.dwPlatformId)
169 {
170 case VER_PLATFORM_WIN32_WINDOWS:
171 if (osv.dwMinorVersion < 10)
172 ver = "95";
173 else if (osv.dwMinorVersion < 90)
174 ver = "98";
175 else
176 ver = "ME";
177 break;
178
179 case VER_PLATFORM_WIN32_NT:
180 switch (osv.dwMajorVersion)
181 {
182 case 5:
183 switch (osv.dwMinorVersion)
184 {
185 case 0: ver = "2000"; break;
186 case 1: ver = "XP"; break;
187 case 2: ver = osv.wProductType == VER_NT_WORKSTATION ? "XP x64" : "Server 2003"; break;
188 }
189 break;
190
191 case 6:
192 {
193 static const char *client[] = { "Vista", "7", "8", "8.1" };
194 static const char *server[] = { "Server 2008", "Server 2008 R2", "Server 2012", "Server 2012 R2" };
195 ver = ((osv.wProductType == VER_NT_WORKSTATION) ? client : server)[osv.dwMinorVersion % ARRAY_SIZE(client)];
196 }
197 break;
198
199 case 10:
200 switch (osv.wProductType)
201 {
202 case VER_NT_WORKSTATION: ver = "10"; break;
203 default: ver = "Server"; break;
204 }
205 break;
206 }
207 break;
208 }
209
210 char *buf = (char *)Xcalloc(1, 256);
211 int len;
212
213 if (ntdll_wine_get_version)
214 len = Bsprintf(buf, "Wine %s, identifying as Windows %s", (char *)ntdll_wine_get_version(), ver);
215 else
216 {
217 len = Bsprintf(buf, "Windows %s", ver);
218
219 if (osv.dwPlatformId != VER_PLATFORM_WIN32_NT || osv.dwMajorVersion < 6)
220 {
221 Bstrcat(buf, " (UNSUPPORTED)");
222 len = Bstrlen(buf);
223 }
224 }
225
226 // service packs
227 if (osv.szCSDVersion[0])
228 {
229 buf[len] = 32;
230 Bstrcat(&buf[len], osv.szCSDVersion);
231 }
232
233 initprintf("Running on %s (build %lu.%lu.%lu)\n", buf, osv.dwMajorVersion, osv.dwMinorVersion, osv.dwBuildNumber);
234 Xfree(buf);
235 }
236
237 //
238 // win_checkinstance() -- looks for another instance of a Build app
239 //
windowsCheckAlreadyRunning(void)240 int windowsCheckAlreadyRunning(void)
241 {
242 if (!g_singleInstanceSemaphore) return 1;
243 return (WaitForSingleObject(g_singleInstanceSemaphore,0) != WAIT_TIMEOUT);
244 }
245
246
247 typedef void (*dllSetString)(const char*);
248
249 //
250 // win_open(), win_init(), win_setvideomode(), win_close() -- shared code
251 //
windowsPreInit(void)252 int windowsPreInit(void)
253 {
254 if (!windowsGetVersion())
255 {
256 windowsShowError("This version of Windows is not supported.");
257 return -1;
258 }
259
260 windowsGetSystemKeyboardLayout();
261 windowsGetSystemKeyboardLayoutName();
262
263 #ifdef DEBUGGINGAIDS
264 HMODULE ebacktrace = LoadLibraryA(EBACKTRACEDLL);
265 if (ebacktrace)
266 {
267 dllSetString SetTechnicalName = (dllSetString)(void(*))GetProcAddress(ebacktrace, "SetTechnicalName");
268 dllSetString SetProperName = (dllSetString)(void(*))GetProcAddress(ebacktrace, "SetProperName");
269
270 if (SetTechnicalName)
271 SetTechnicalName(AppTechnicalName);
272
273 if (SetProperName)
274 SetProperName(AppProperName);
275 }
276 #endif
277
278 g_singleInstanceSemaphore = CreateSemaphore(NULL, 1,1, WindowClass);
279
280 return 0;
281 }
282
osdcmd_win_systemtimermode(osdcmdptr_t parm)283 static int osdcmd_win_systemtimermode(osdcmdptr_t parm)
284 {
285 int const r = osdcmd_cvar_set(parm);
286
287 if (r != OSDCMD_OK)
288 return r;
289
290 windowsSetupTimer(win_systemtimermode);
291
292 return OSDCMD_OK;
293 }
294
windowsPlatformInit(void)295 void windowsPlatformInit(void)
296 {
297 static osdcvardata_t cvars_win[] = {
298 { "win_togglecomposition", "disables Windows Vista/7 DWM composition", (void *)&win_togglecomposition, CVAR_BOOL, 0, 1 },
299
300 { "win_priorityclass",
301 "Windows process priority class:\n"
302 " -1: do not alter process priority\n"
303 " 0: HIGH when game has focus, NORMAL when interacting with other programs\n"
304 " 1: NORMAL when game has focus, IDLE when interacting with other programs",
305 (void *)&win_priorityclass, CVAR_INT, -1, 1 },
306
307 { "win_performancemode",
308 "Windows performance mode:\n"
309 " 0: do not alter performance mode\n"
310 " 1: use HIGH PERFORMANCE power plan when game has focus",
311 (void *)&win_performancemode, CVAR_BOOL, 0, 1 },
312
313 };
314
315 static osdcvardata_t win_timer_cvar = { "win_systemtimermode",
316 "Windows timer interrupt resolution:\n"
317 " 0: 1.0ms\n"
318 " 1: 0.5ms low-latency"
319 #if defined RENDERTYPESDL && SDL_MAJOR_VERSION >= 2
320 "\nThis option has no effect when running on battery power.",
321 #else
322 ,
323 #endif
324 (void *)&win_systemtimermode, CVAR_BOOL, 0, 1 };
325
326 OSD_RegisterCvar(&win_timer_cvar, osdcmd_win_systemtimermode);
327
328 for (int i=0; i<ARRAY_SSIZE(cvars_win); i++)
329 OSD_RegisterCvar(&cvars_win[i], osdcmd_cvar_set);
330
331 windowsPrintVersion();
332 windowsSetupTimer(0);
333
334 if (osv.dwMajorVersion >= 6)
335 {
336 if (!hPOWRPROF && (hPOWRPROF = GetModuleHandle("powrprof.dll")))
337 {
338 powrprof_PowerGetActiveScheme = (PFNPOWERGETACTIVESCHEME)(void(*))GetProcAddress(hPOWRPROF, "PowerGetActiveScheme");
339 powrprof_PowerSetActiveScheme = (PFNPOWERSETACTIVESCHEME)(void(*))GetProcAddress(hPOWRPROF, "PowerSetActiveScheme");
340
341 if (powrprof_PowerGetActiveScheme == nullptr || powrprof_PowerSetActiveScheme == nullptr)
342 OSD_Printf("ERROR: unable to locate PowerGetActiveScheme or PowerSetActiveScheme symbols in powrprof.dll!\n");
343 else if (!systemPowerSchemeGUID)
344 powrprof_PowerGetActiveScheme(NULL, &systemPowerSchemeGUID);
345 }
346 }
347 }
348
349 typedef UINT D3DKMT_HANDLE;
350 typedef UINT D3DDDI_VIDEO_PRESENT_SOURCE_ID;
351
352 typedef struct _D3DKMT_OPENADAPTERFROMHDC
353 {
354 HDC hDc;
355 D3DKMT_HANDLE hAdapter;
356 LUID AdapterLuid;
357
358 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
359 } D3DKMT_OPENADAPTERFROMHDC;
360
361 typedef struct _D3DKMT_CLOSEADAPTER
362 {
363 D3DKMT_HANDLE hAdapter;
364 } D3DKMT_CLOSEADAPTER;
365
366 typedef struct _D3DKMT_WAITFORVERTICALBLANKEVENT
367 {
368 D3DKMT_HANDLE hAdapter;
369 D3DKMT_HANDLE hDevice;
370
371 D3DDDI_VIDEO_PRESENT_SOURCE_ID VidPnSourceId;
372 } D3DKMT_WAITFORVERTICALBLANKEVENT;
373
374 typedef NTSTATUS(APIENTRY *PFND3DKMTOPENADAPTERFROMHDC)(D3DKMT_OPENADAPTERFROMHDC *);
375 typedef NTSTATUS(APIENTRY *PFND3DKMTCLOSEADAPTER)(D3DKMT_CLOSEADAPTER *);
376 typedef NTSTATUS(APIENTRY *PFND3DKMTWAITFORVERTICALBLANKEVENT)(D3DKMT_WAITFORVERTICALBLANKEVENT *);
377
378 typedef HRESULT(WINAPI *PFNDWMENABLECOMPOSITION)(UINT);
379 typedef HRESULT(WINAPI *PFNDWMGETCOMPOSITIONTIMINGINFO)(HWND, DWM_TIMING_INFO *);
380 typedef HRESULT(WINAPI *PFNDWMISCOMPOSITIONENABLED)(BOOL *);
381 typedef HRESULT(WINAPI *PFNDWMFLUSH)(void);
382
383 static HMODULE hDWMApi;
384 static PFNDWMFLUSH dwmapi_DwmFlush;
385 static PFNDWMISCOMPOSITIONENABLED dwmapi_DwmIsCompositionEnabled;
386
windowsWaitForVBlank(void)387 void windowsWaitForVBlank(void)
388 {
389 // if we don't have these, we aren't going to have the WDDM functions below either, so bailing here is fine.
390 if (osv.dwMajorVersion < 6 || !dwmapi_DwmFlush || !dwmapi_DwmIsCompositionEnabled)
391 return;
392
393 static int useDWMsync;
394
395 // here comes the voodoo bullshit ;)
396 static HMODULE hGDI32;
397 static PFND3DKMTOPENADAPTERFROMHDC gdi32_D3DKMTOpenAdapterFromHdc;
398 static PFND3DKMTCLOSEADAPTER gdi32_D3DKMTCloseAdapter;
399 static PFND3DKMTWAITFORVERTICALBLANKEVENT gdi32_D3DKMTWaitForVBlank;
400
401 if (!hGDI32 && (hGDI32 = GetModuleHandle("gdi32.dll")))
402 {
403 gdi32_D3DKMTOpenAdapterFromHdc = (PFND3DKMTOPENADAPTERFROMHDC) (void(*))GetProcAddress(hGDI32, "D3DKMTOpenAdapterFromHdc");
404 gdi32_D3DKMTCloseAdapter = (PFND3DKMTCLOSEADAPTER) (void(*))GetProcAddress(hGDI32, "D3DKMTCloseAdapter");
405 gdi32_D3DKMTWaitForVBlank = (PFND3DKMTWAITFORVERTICALBLANKEVENT) (void(*))GetProcAddress(hGDI32, "D3DKMTWaitForVerticalBlankEvent");
406 }
407
408 if (useDWMsync || !fullscreen || !gdi32_D3DKMTOpenAdapterFromHdc || !gdi32_D3DKMTCloseAdapter || !gdi32_D3DKMTWaitForVBlank)
409 {
410 dwm:
411 // if we don't have the better APIs but composition is enabled, this is sometimes good enough
412 BOOL compositionEnabled = false;
413
414 if (SUCCEEDED(dwmapi_DwmIsCompositionEnabled(&compositionEnabled)) && compositionEnabled && dwmapi_DwmFlush() != S_OK)
415 OSD_Printf("debug: DWM flush FAILED!\n");
416
417 return;
418 }
419
420 MONITORINFOEX mi = {};
421 mi.cbSize = sizeof(mi);
422 GetMonitorInfo(MonitorFromWindow(win_gethwnd(), MONITOR_DEFAULTTONULL), &mi);
423
424 D3DKMT_OPENADAPTERFROMHDC activeAdapter = {};
425 activeAdapter.hDc = CreateDC(mi.szDevice, mi.szDevice, nullptr, nullptr);
426
427 if (activeAdapter.hDc == nullptr)
428 {
429 OSD_Printf("debug: CreateDC() FAILED! display: %s windowx: %d windowy: %d\n", mi.szDevice, windowx, windowy);
430 useDWMsync = 1;
431 goto dwm;
432 }
433
434 auto status = gdi32_D3DKMTOpenAdapterFromHdc(&activeAdapter);
435 DeleteDC(activeAdapter.hDc);
436
437 if (NT_SUCCESS(status))
438 {
439 D3DKMT_WAITFORVERTICALBLANKEVENT vBlankEvent = { activeAdapter.hAdapter, 0, activeAdapter.VidPnSourceId };
440
441 if (NT_SUCCESS(status = gdi32_D3DKMTWaitForVBlank(&vBlankEvent)))
442 {
443 // the D3DKMT_CLOSEADAPTER struct only contains one member, and it's
444 // the same as the first member in D3DKMT_WAITFORVERTICALBLANKEVENT
445 if (NT_SUCCESS(status = gdi32_D3DKMTCloseAdapter((D3DKMT_CLOSEADAPTER *)&vBlankEvent)))
446 return;
447 else
448 OSD_Printf("debug: D3DKMTCloseAdapter() FAILED! NTSTATUS: 0x%x\n", (unsigned)status);
449 }
450 else
451 OSD_Printf("debug: D3DKMTWaitForVerticalBlankEvent() FAILED! NTSTATUS: 0x%x\n", (unsigned)status);
452 }
453 else
454 OSD_Printf("debug: D3DKMTOpenAdapterFromHdc() FAILED! NTSTATUS: 0x%x\n", (unsigned)status);
455
456 OSD_Printf("debug: D3DKMT failure, falling back to DWM sync\n");
457 useDWMsync = 1;
458 goto dwm;
459 }
460
windowsDwmSetupComposition(int const compEnable)461 void windowsDwmSetupComposition(int const compEnable)
462 {
463 if (osv.dwMajorVersion < 6)
464 return;
465
466 static PFNDWMENABLECOMPOSITION dwmapi_DwmEnableComposition;
467 static PFNDWMGETCOMPOSITIONTIMINGINFO dwmapi_DwmGetCompositionTimingInfo;
468
469 if (!hDWMApi && (hDWMApi = GetModuleHandle("dwmapi.dll")))
470 {
471 dwmapi_DwmEnableComposition = (PFNDWMENABLECOMPOSITION) (void(*))GetProcAddress(hDWMApi, "DwmEnableComposition");
472 dwmapi_DwmFlush = (PFNDWMFLUSH) (void(*))GetProcAddress(hDWMApi, "DwmFlush");
473 dwmapi_DwmGetCompositionTimingInfo = (PFNDWMGETCOMPOSITIONTIMINGINFO) (void(*))GetProcAddress(hDWMApi, "DwmGetCompositionTimingInfo");
474 dwmapi_DwmIsCompositionEnabled = (PFNDWMISCOMPOSITIONENABLED) (void(*))GetProcAddress(hDWMApi, "DwmIsCompositionEnabled");
475 }
476
477 if (dwmapi_DwmGetCompositionTimingInfo)
478 {
479 timingInfo = {};
480 timingInfo.cbSize = sizeof(DWM_TIMING_INFO);
481
482 // the HWND parameter was deprecated in Windows 8.1 because DWM always syncs to the primary monitor's refresh...
483
484 HRESULT result = dwmapi_DwmGetCompositionTimingInfo(nullptr, &timingInfo);
485
486 if (FAILED(result))
487 OSD_Printf("debug: DwmGetCompositionTimingInfo() FAILED! HRESULT: 0x%x (%s)\n", (unsigned)result, std::system_category().message(result).c_str());
488 }
489
490 if (win_togglecomposition && dwmapi_DwmEnableComposition && osv.dwMinorVersion < 2)
491 {
492 dwmapi_DwmEnableComposition(compEnable);
493
494 if (!win_silentvideomodeswitch)
495 OSD_Printf("%sabling DWM desktop composition...\n", (compEnable) ? "En" : "Dis");
496 }
497 }
498
windowsPlatformCleanup(void)499 void windowsPlatformCleanup(void)
500 {
501 if (g_singleInstanceSemaphore)
502 CloseHandle(g_singleInstanceSemaphore);
503
504 windowsSetKeyboardLayout(windowsGetSystemKeyboardLayoutName());
505
506 if (systemPowerSchemeGUID)
507 {
508 powrprof_PowerSetActiveScheme(NULL, systemPowerSchemeGUID);
509 LocalFree(systemPowerSchemeGUID);
510 }
511 }
512
513
514 //
515 // GetWindowsErrorMsg() -- gives a pointer to a static buffer containing the Windows error message
516 //
windowsGetErrorMessage(DWORD code)517 LPTSTR windowsGetErrorMessage(DWORD code)
518 {
519 static TCHAR lpMsgBuf[1024];
520
521 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
522 NULL, code,
523 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
524 (LPTSTR)lpMsgBuf, 1024, NULL);
525
526 return lpMsgBuf;
527 }
528
529
530 // Keyboard layout switching
531
windowsDecodeKeyboardLayoutName(char const * keyboardLayout)532 static char const * windowsDecodeKeyboardLayoutName(char const * keyboardLayout)
533 {
534 int const localeID = Bstrtol(keyboardLayout, NULL, 16);
535 static char localeName[16];
536
537 int const result = GetLocaleInfo(MAKELCID(localeID, SORT_DEFAULT), LOCALE_SNAME, localeName, ARRAY_SIZE(localeName));
538
539 if (!result)
540 {
541 OSD_Printf("Error decoding name for locale ID %d: %s\n", localeID, windowsGetErrorMessage(GetLastError()));
542 return keyboardLayout;
543 }
544
545 return localeName;
546 }
547
windowsSetKeyboardLayout(char const * layout,int focusChanged)548 void windowsSetKeyboardLayout(char const *layout, int focusChanged /*= 0*/)
549 {
550 char layoutName[KL_NAMELENGTH];
551
552 GetKeyboardLayoutName(layoutName);
553
554 if (!Bstrcmp(layoutName, layout))
555 return;
556
557 //if (!win_silentfocuschange)
558 {
559 if (focusChanged)
560 OSD_Printf("Focus change: ");
561
562 if (layout == enUSLayoutString)
563 OSD_Printf("Loaded %s keyboard layout\n", windowsDecodeKeyboardLayoutName(layout));
564 else
565 OSD_Printf("Restored %s keyboard layout\n", windowsDecodeKeyboardLayoutName(layout));
566 }
567
568 static int enUSLoaded;
569 static HKL enUSLayout;
570
571 if (layout == enUSLayoutString)
572 {
573 if (enUSLoaded)
574 ActivateKeyboardLayout(enUSLayout, KLF_SETFORPROCESS);
575 else if ((enUSLayout = LoadKeyboardLayout(enUSLayoutString, KLF_ACTIVATE | KLF_SETFORPROCESS | KLF_SUBSTITUTE_OK)))
576 enUSLoaded = true;
577 }
578 else
579 ActivateKeyboardLayout(windowsGetSystemKeyboardLayout(), KLF_SETFORPROCESS);
580 }
581
582
windowsGetSystemKeyboardLayoutName(void)583 char *windowsGetSystemKeyboardLayoutName(void)
584 {
585 static char systemLayoutName[KL_NAMELENGTH];
586 static int layoutSaved;
587
588 if (!layoutSaved)
589 {
590 if (!GetKeyboardLayoutName(systemLayoutName))
591 OSD_Printf("Error determining system keyboard layout: %s\n", windowsGetErrorMessage(GetLastError()));
592
593 layoutSaved = true;
594 }
595
596 return systemLayoutName;
597 }
598
windowsGetSystemKeyboardLayout(void)599 HKL windowsGetSystemKeyboardLayout(void)
600 {
601 static HKL systemLayout;
602 static int layoutSaved;
603
604 if (!layoutSaved)
605 {
606 systemLayout = GetKeyboardLayout(0);
607 layoutSaved = true;
608 }
609
610 return systemLayout;
611 }
612
windowsHandleFocusChange(int const appactive)613 void windowsHandleFocusChange(int const appactive)
614 {
615 #ifndef DEBUGGINGAIDS
616 win_silentfocuschange = true;
617 #endif
618
619 if (appactive)
620 {
621 if (win_priorityclass != -1)
622 SetPriorityClass(GetCurrentProcess(), win_priorityclass ? BELOW_NORMAL_PRIORITY_CLASS : HIGH_PRIORITY_CLASS);
623
624 windowsSetupTimer(win_systemtimermode);
625 windowsSetKeyboardLayout(enUSLayoutString, true);
626
627 if (win_performancemode && systemPowerSchemeGUID)
628 powrprof_PowerSetActiveScheme(NULL, &GUID_MIN_POWER_SAVINGS);
629 }
630 else
631 {
632 if (win_priorityclass != -1)
633 SetPriorityClass(GetCurrentProcess(), win_priorityclass ? IDLE_PRIORITY_CLASS : ABOVE_NORMAL_PRIORITY_CLASS);
634
635 windowsSetupTimer(0);
636 windowsSetKeyboardLayout(windowsGetSystemKeyboardLayoutName(), true);
637
638 if (systemPowerSchemeGUID)
639 powrprof_PowerSetActiveScheme(NULL, systemPowerSchemeGUID);
640 }
641
642 win_silentfocuschange = false;
643 }
644
645 //
646 // ShowErrorBox() -- shows an error message box
647 //
windowsShowError(const char * m)648 void windowsShowError(const char *m)
649 {
650 TCHAR msg[1024];
651
652 wsprintf(msg, "%s: %s", m, windowsGetErrorMessage(GetLastError()));
653 MessageBox(0, msg, apptitle, MB_OK|MB_ICONSTOP);
654 }
655
656
657 //
658 // Miscellaneous
659 //
windowsGetCommandLine(char ** argvbuf)660 int windowsGetCommandLine(char **argvbuf)
661 {
662 int buildargc = 0;
663
664 *argvbuf = Xstrdup(GetCommandLine());
665
666 if (*argvbuf)
667 {
668 char quoted = 0, instring = 0, swallownext = 0;
669 char *wp;
670 for (const char *p = wp = *argvbuf; *p; p++)
671 {
672 if (*p == ' ')
673 {
674 if (instring)
675 {
676 if (!quoted)
677 {
678 // end of a string
679 *(wp++) = 0;
680 instring = 0;
681 }
682 else
683 *(wp++) = *p;
684 }
685 }
686 else if (*p == '"' && !swallownext)
687 {
688 if (instring)
689 {
690 if (quoted && p[1] == ' ')
691 {
692 // end of a string
693 *(wp++) = 0;
694 instring = 0;
695 }
696 quoted = !quoted;
697 }
698 else
699 {
700 instring = 1;
701 quoted = 1;
702 buildargc++;
703 }
704 }
705 else if (*p == '\\' && p[1] == '"' && !swallownext)
706 swallownext = 1;
707 else
708 {
709 if (!instring)
710 buildargc++;
711
712 instring = 1;
713 *(wp++) = *p;
714 swallownext = 0;
715 }
716 }
717 *wp = 0;
718 }
719
720 return buildargc;
721 }
722