mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-27 04:00:42 +00:00
494 lines
12 KiB
C++
494 lines
12 KiB
C++
// Windows layer-independent code
|
|
|
|
#include "compat.h"
|
|
#include "build.h"
|
|
#include "baselayer.h"
|
|
#include "osd.h"
|
|
#include "cache1d.h"
|
|
|
|
|
|
|
|
#include "winbits.h"
|
|
|
|
#ifdef BITNESS64
|
|
# define EBACKTRACEDLL "ebacktrace1-64.dll"
|
|
#else
|
|
# define EBACKTRACEDLL "ebacktrace1.dll"
|
|
#endif
|
|
|
|
int32_t backgroundidle = 1;
|
|
|
|
int64_t win_timerfreq = 0;
|
|
|
|
char silentvideomodeswitch = 0;
|
|
|
|
static char taskswitching = 1;
|
|
|
|
static HANDLE instanceflag = NULL;
|
|
|
|
static OSVERSIONINFOEX osv;
|
|
|
|
static int32_t togglecomp = 0;
|
|
|
|
FARPROC pwinever;
|
|
|
|
//
|
|
// CheckWinVersion() -- check to see what version of Windows we happen to be running under
|
|
//
|
|
BOOL CheckWinVersion(void)
|
|
{
|
|
HMODULE hntdll = GetModuleHandle("ntdll.dll");
|
|
|
|
if (hntdll)
|
|
pwinever = GetProcAddress(hntdll, "wine_get_version");
|
|
|
|
ZeroMemory(&osv, sizeof(osv));
|
|
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
|
|
if (!GetVersionEx((LPOSVERSIONINFOA)&osv)) return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void win_printversion(void)
|
|
{
|
|
const char *ver = "";
|
|
|
|
switch (osv.dwPlatformId)
|
|
{
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
if (osv.dwMinorVersion < 10)
|
|
ver = "95";
|
|
else if (osv.dwMinorVersion < 90)
|
|
ver = "98";
|
|
else
|
|
ver = "ME";
|
|
break;
|
|
|
|
case VER_PLATFORM_WIN32_NT:
|
|
switch (osv.dwMajorVersion)
|
|
{
|
|
case 5:
|
|
switch (osv.dwMinorVersion)
|
|
{
|
|
case 0: ver = "2000"; break;
|
|
case 1: ver = "XP"; break;
|
|
case 2: ver = osv.wProductType == VER_NT_WORKSTATION ? "XP x64" : "Server 2003"; break;
|
|
}
|
|
break;
|
|
|
|
case 6:
|
|
switch (osv.dwMinorVersion)
|
|
{
|
|
case 0: ver = osv.wProductType == VER_NT_WORKSTATION ? "Vista" : "Server 2008"; break;
|
|
case 1: ver = osv.wProductType == VER_NT_WORKSTATION ? "7" : "Server 2008 R2"; break;
|
|
case 2: ver = osv.wProductType == VER_NT_WORKSTATION ? "8" : "Server 2012"; break;
|
|
case 3: ver = osv.wProductType == VER_NT_WORKSTATION ? "8.1" : "Server 2012 R2"; break;
|
|
}
|
|
break;
|
|
|
|
case 10:
|
|
switch (osv.dwMinorVersion)
|
|
{
|
|
case 0: ver = osv.wProductType == VER_NT_WORKSTATION ? "10" : "Server 2016"; break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
char *str = (char *)Xcalloc(1, 256);
|
|
int l;
|
|
|
|
if (pwinever)
|
|
l = Bsprintf(str, "Wine %s identifying as Windows %s", (char *)pwinever(), ver);
|
|
else
|
|
l = Bsprintf(str, "Windows %s", ver);
|
|
|
|
// service packs
|
|
if (osv.szCSDVersion[0])
|
|
{
|
|
str[l] = 32;
|
|
Bstrcat(&str[l], osv.szCSDVersion);
|
|
}
|
|
|
|
initprintf("Running on %s (build %lu.%lu.%lu)\n", str, osv.dwMajorVersion, osv.dwMinorVersion, osv.dwBuildNumber);
|
|
Bfree(str);
|
|
}
|
|
|
|
//
|
|
// win_allowtaskswitching() -- captures/releases alt+tab hotkeys
|
|
//
|
|
void win_allowtaskswitching(int32_t onf)
|
|
{
|
|
if (onf == taskswitching) return;
|
|
taskswitching = onf;
|
|
|
|
if (onf)
|
|
{
|
|
UnregisterHotKey(0,0);
|
|
UnregisterHotKey(0,1);
|
|
}
|
|
else
|
|
{
|
|
RegisterHotKey(0,0,MOD_ALT,VK_TAB);
|
|
RegisterHotKey(0,1,MOD_ALT|MOD_SHIFT,VK_TAB);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// win_checkinstance() -- looks for another instance of a Build app
|
|
//
|
|
int32_t win_checkinstance(void)
|
|
{
|
|
if (!instanceflag) return 0;
|
|
return (WaitForSingleObject(instanceflag,0) == WAIT_TIMEOUT);
|
|
}
|
|
|
|
//
|
|
// high-resolution timers for profiling
|
|
//
|
|
#if defined(RENDERTYPEWIN) || SDL_MAJOR_VERSION==1
|
|
int32_t win_inittimer(void)
|
|
{
|
|
int64_t t;
|
|
|
|
if (win_timerfreq) return 0; // already installed
|
|
|
|
// OpenWatcom seems to want us to query the value into a local variable
|
|
// instead of the global 'win_timerfreq' or else it gets pissed with an
|
|
// access violation
|
|
if (!QueryPerformanceFrequency((LARGE_INTEGER *)&t))
|
|
{
|
|
ShowErrorBox("Failed fetching timer frequency");
|
|
return -1;
|
|
}
|
|
win_timerfreq = t;
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t win_getu64ticks(void)
|
|
{
|
|
uint64_t i;
|
|
if (win_timerfreq == 0) return 0;
|
|
QueryPerformanceCounter((LARGE_INTEGER *)&i);
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
|
|
static void ToggleDesktopComposition(BOOL compEnable)
|
|
{
|
|
static HMODULE hDWMApiDLL = NULL;
|
|
static HRESULT(WINAPI *aDwmEnableComposition)(UINT);
|
|
|
|
if (!hDWMApiDLL && (hDWMApiDLL = LoadLibrary("DWMAPI.DLL")))
|
|
aDwmEnableComposition = (HRESULT(WINAPI *)(UINT))(void (*)(void))GetProcAddress(hDWMApiDLL, "DwmEnableComposition");
|
|
|
|
if (aDwmEnableComposition)
|
|
{
|
|
aDwmEnableComposition(compEnable);
|
|
if (!silentvideomodeswitch)
|
|
initprintf("%sabling desktop composition...\n", (compEnable) ? "En" : "Dis");
|
|
}
|
|
}
|
|
|
|
typedef void (*dllSetString)(const char*);
|
|
|
|
//
|
|
// win_open(), win_init(), win_setvideomode(), win_uninit(), win_close() -- shared code
|
|
//
|
|
void win_open(void)
|
|
{
|
|
#ifdef DEBUGGINGAIDS
|
|
HMODULE ebacktrace = LoadLibraryA(EBACKTRACEDLL);
|
|
if (ebacktrace)
|
|
{
|
|
dllSetString SetTechnicalName = (dllSetString) (void (*)(void))GetProcAddress(ebacktrace, "SetTechnicalName");
|
|
dllSetString SetProperName = (dllSetString) (void (*)(void))GetProcAddress(ebacktrace, "SetProperName");
|
|
|
|
if (SetTechnicalName)
|
|
SetTechnicalName(AppTechnicalName);
|
|
|
|
if (SetProperName)
|
|
SetProperName(AppProperName);
|
|
}
|
|
#endif
|
|
|
|
instanceflag = CreateSemaphore(NULL, 1,1, WindowClass);
|
|
}
|
|
|
|
void win_init(void)
|
|
{
|
|
uint32_t i;
|
|
|
|
static osdcvardata_t cvars_win[] =
|
|
{
|
|
{ "r_togglecomposition","enable/disable toggle of desktop composition when initializing screen modes",(void *) &togglecomp, CVAR_BOOL, 0, 1 },
|
|
};
|
|
|
|
for (i=0; i<ARRAY_SIZE(cvars_win); i++)
|
|
OSD_RegisterCvar(&cvars_win[i], osdcmd_cvar_set);
|
|
|
|
win_printversion();
|
|
}
|
|
|
|
void win_setvideomode(int32_t c)
|
|
{
|
|
if (togglecomp && osv.dwMajorVersion == 6 && osv.dwMinorVersion < 2)
|
|
ToggleDesktopComposition(c < 16);
|
|
}
|
|
|
|
void win_uninit(void)
|
|
{
|
|
win_allowtaskswitching(1);
|
|
}
|
|
|
|
void win_close(void)
|
|
{
|
|
if (instanceflag) CloseHandle(instanceflag);
|
|
}
|
|
|
|
|
|
// Keyboard layout switching (disable because this is rude.)
|
|
|
|
static void switchlayout(char const * layout)
|
|
{
|
|
/*
|
|
char layoutname[KL_NAMELENGTH];
|
|
|
|
GetKeyboardLayoutName(layoutname);
|
|
|
|
if (!Bstrcmp(layoutname, layout))
|
|
return;
|
|
|
|
initprintf("Switching keyboard layout from %s to %s\n", layoutname, layout);
|
|
LoadKeyboardLayout(layout, KLF_ACTIVATE|KLF_SETFORPROCESS|KLF_SUBSTITUTE_OK);
|
|
*/
|
|
}
|
|
|
|
static char OriginalLayoutName[KL_NAMELENGTH];
|
|
|
|
void Win_GetOriginalLayoutName(void)
|
|
{
|
|
//GetKeyboardLayoutName(OriginalLayoutName);
|
|
}
|
|
|
|
void Win_SetKeyboardLayoutUS(int const toggle)
|
|
{
|
|
/*
|
|
static int currentstate;
|
|
|
|
if (toggle != currentstate)
|
|
{
|
|
if (toggle)
|
|
{
|
|
// 00000409 is "American English"
|
|
switchlayout("00000409");
|
|
currentstate = toggle;
|
|
}
|
|
else if (OriginalLayoutName[0])
|
|
{
|
|
switchlayout(OriginalLayoutName);
|
|
currentstate = toggle;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
//
|
|
// ShowErrorBox() -- shows an error message box
|
|
//
|
|
void ShowErrorBox(const char *m)
|
|
{
|
|
TCHAR msg[1024];
|
|
|
|
wsprintf(msg, "%s: %s", m, GetWindowsErrorMsg(GetLastError()));
|
|
MessageBox(0, msg, apptitle, MB_OK|MB_ICONSTOP);
|
|
}
|
|
|
|
//
|
|
// GetWindowsErrorMsg() -- gives a pointer to a static buffer containing the Windows error message
|
|
//
|
|
LPTSTR GetWindowsErrorMsg(DWORD code)
|
|
{
|
|
static TCHAR lpMsgBuf[1024];
|
|
|
|
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, code,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR)lpMsgBuf, 1024, NULL);
|
|
|
|
return lpMsgBuf;
|
|
}
|
|
|
|
|
|
//
|
|
// Miscellaneous
|
|
//
|
|
|
|
int32_t addsearchpath_ProgramFiles(const char *p)
|
|
{
|
|
int32_t returncode = -1, i;
|
|
const char *ProgramFiles[2] = { Bgetenv("ProgramFiles"), Bgetenv("ProgramFiles(x86)") };
|
|
|
|
for (i = 0; i < 2; ++i)
|
|
{
|
|
if (ProgramFiles[i])
|
|
{
|
|
char *buffer = (char*)Xmalloc((strlen(ProgramFiles[i])+1+strlen(p)+1)*sizeof(char));
|
|
Bsprintf(buffer,"%s/%s",ProgramFiles[i],p);
|
|
if (addsearchpath(buffer) == 0) // if any work, return success
|
|
returncode = 0;
|
|
Bfree(buffer);
|
|
}
|
|
}
|
|
|
|
return returncode;
|
|
}
|
|
|
|
int32_t win_buildargs(char **argvbuf)
|
|
{
|
|
int32_t buildargc = 0;
|
|
|
|
*argvbuf = Xstrdup(GetCommandLine());
|
|
|
|
if (*argvbuf)
|
|
{
|
|
char quoted = 0, instring = 0, swallownext = 0;
|
|
char *wp;
|
|
for (const char *p = wp = *argvbuf; *p; p++)
|
|
{
|
|
if (*p == ' ')
|
|
{
|
|
if (instring)
|
|
{
|
|
if (!quoted)
|
|
{
|
|
// end of a string
|
|
*(wp++) = 0;
|
|
instring = 0;
|
|
}
|
|
else
|
|
*(wp++) = *p;
|
|
}
|
|
}
|
|
else if (*p == '"' && !swallownext)
|
|
{
|
|
if (instring)
|
|
{
|
|
if (quoted && p[1] == ' ')
|
|
{
|
|
// end of a string
|
|
*(wp++) = 0;
|
|
instring = 0;
|
|
}
|
|
quoted = !quoted;
|
|
}
|
|
else
|
|
{
|
|
instring = 1;
|
|
quoted = 1;
|
|
buildargc++;
|
|
}
|
|
}
|
|
else if (*p == '\\' && p[1] == '"' && !swallownext)
|
|
swallownext = 1;
|
|
else
|
|
{
|
|
if (!instring)
|
|
buildargc++;
|
|
|
|
instring = 1;
|
|
*(wp++) = *p;
|
|
swallownext = 0;
|
|
}
|
|
}
|
|
*wp = 0;
|
|
}
|
|
|
|
return buildargc;
|
|
}
|
|
|
|
|
|
//==========================================================================
|
|
//
|
|
// CalculateCPUSpeed
|
|
//
|
|
// Make a decent guess at how much time elapses between TSC steps. This can
|
|
// vary over runtime depending on power management settings, so should not
|
|
// be used anywhere that truely accurate timing actually matters.
|
|
//
|
|
//==========================================================================
|
|
|
|
double PerfToSec, PerfToMillisec;
|
|
#include "stats.h"
|
|
|
|
static void CalculateCPUSpeed()
|
|
{
|
|
LARGE_INTEGER freq;
|
|
|
|
QueryPerformanceFrequency(&freq);
|
|
|
|
if (freq.QuadPart != 0)
|
|
{
|
|
LARGE_INTEGER count1, count2;
|
|
cycle_t ClockCalibration;
|
|
DWORD min_diff;
|
|
|
|
ClockCalibration.Reset();
|
|
|
|
// Count cycles for at least 55 milliseconds.
|
|
// The performance counter may be very low resolution compared to CPU
|
|
// speeds today, so the longer we count, the more accurate our estimate.
|
|
// On the other hand, we don't want to count too long, because we don't
|
|
// want the user to notice us spend time here, since most users will
|
|
// probably never use the performance statistics.
|
|
min_diff = freq.LowPart * 11 / 200;
|
|
|
|
// Minimize the chance of task switching during the testing by going very
|
|
// high priority. This is another reason to avoid timing for too long.
|
|
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
|
|
|
// Make sure we start timing on a counter boundary.
|
|
QueryPerformanceCounter(&count1);
|
|
do
|
|
{
|
|
QueryPerformanceCounter(&count2);
|
|
} while (count1.QuadPart == count2.QuadPart);
|
|
|
|
// Do the timing loop.
|
|
ClockCalibration.Clock();
|
|
do
|
|
{
|
|
QueryPerformanceCounter(&count1);
|
|
} while ((count1.QuadPart - count2.QuadPart) < min_diff);
|
|
ClockCalibration.Unclock();
|
|
|
|
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
|
|
|
PerfToSec = double(count1.QuadPart - count2.QuadPart) / (double(ClockCalibration.GetRawCounter()) * freq.QuadPart);
|
|
PerfToMillisec = PerfToSec * 1000.0;
|
|
}
|
|
}
|
|
|
|
class Initer
|
|
{
|
|
public:
|
|
Initer() { CalculateCPUSpeed(); }
|
|
};
|
|
|
|
static Initer initer;
|
|
|
|
|
|
// Workaround for a bug in mingwrt-4.0.0 and up where a function named main() in misc/src/libcrt/gdtoa/qnan.c takes precedence over the proper one in src/libcrt/crt/main.c.
|
|
#if (defined __MINGW32__ && EDUKE32_GCC_PREREQ(4,8)) || EDUKE32_CLANG_PREREQ(3,4)
|
|
# undef main
|
|
# include "mingw_main.cpp"
|
|
#endif
|