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