Subversion Repositories eduke32

Rev

Rev 8631 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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