From 69a1a0d6fd9165182eba43924a807adf35fd0565 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Mon, 24 Mar 2025 19:11:51 -0600 Subject: [PATCH 01/10] win32 backend --- build.zig | 9 +- src/platforms/rcore_desktop_win32.c | 2006 +++++++++++++++++++++++++++ src/rcore.c | 4 + 3 files changed, 2016 insertions(+), 3 deletions(-) create mode 100644 src/platforms/rcore_desktop_win32.c diff --git a/build.zig b/build.zig index b3da65984..39e7593f5 100644 --- a/build.zig +++ b/build.zig @@ -17,7 +17,8 @@ fn setDesktopPlatform(raylib: *std.Build.Step.Compile, platform: PlatformBackend .rgfw => raylib.root_module.addCMacro("PLATFORM_DESKTOP_RGFW", ""), .sdl => raylib.root_module.addCMacro("PLATFORM_DESKTOP_SDL", ""), .android => raylib.root_module.addCMacro("PLATFORM_ANDROID", ""), - else => {}, + .drm => {}, + .win32 => raylib.root_module.addCMacro("PLATFORM_DESKTOP_WIN32", ""), } } @@ -180,11 +181,12 @@ fn compileRaylib(b: *std.Build, target: std.Build.ResolvedTarget, optimize: std. raylib.addIncludePath(b.path("src/platforms")); switch (target.result.os.tag) { .windows => { + try raylib_flags_arr.append("-DUNICODE"); switch (options.platform) { .glfw => try c_source_files.append("src/rglfw.c"), - .rgfw, .sdl, .drm, .android => {}, + .rgfw, .sdl, .drm, .android, .win32 => {}, } - + raylib.linkSystemLibrary("shcore"); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -440,6 +442,7 @@ pub const PlatformBackend = enum { sdl, drm, android, + win32, }; pub fn build(b: *std.Build) !void { diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c new file mode 100644 index 000000000..ef790f655 --- /dev/null +++ b/src/platforms/rcore_desktop_win32.c @@ -0,0 +1,2006 @@ +/********************************************************************************************** +* +* rcore_desktop_win32 - Functions to manage window, graphics device and inputs +* +* PLATFORM: DESKTOP: WIN32 +* - Windows (Win32, Win64) +* +* LIMITATIONS: +* - currently in initial development stage, alot is missing +* - unsure how to support MOUSE_BUTTON_FORWARD/MOUSE_BUTTON_BACK +* +* POSSIBLE IMPROVEMENTS: +* +* ADDITIONAL NOTES: +* +* CONFIGURATION: +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- +* +* DEPENDENCIES: +* - the win32 API, i.e. windows.h +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +// move windows.h functions to new names to avoid redefining the same functions as raylib +#define CloseWindow CloseWindowWin32 +#define Rectangle RectangleWin32 +#define ShowCursor ShowCursorWin32 +#define LoadImageA LoadImageAWin32 +#define LoadImageW LoadImageWin32 +#define DrawTextA DrawTextAWin32 +#define DrawTextW DrawTextWin32 +#define DrawTextExA DrawTextExAWin32 +#define DrawTextExW DrawTextExWin32 + +#define WIN32_LEAN_AND_MEAN +#include + +#undef CloseWindow +#undef Rectangle +#undef ShowCursor +#undef LoadImage +#undef LoadImageA +#undef LoadImageW +#undef DrawText +#undef DrawTextA +#undef DrawTextW +#undef DrawTextEx +#undef DrawTextExA +#undef DrawTextExW + +#include +#include +#include + +#include +#include + +// undefined the glad_glGetString macro definition +#undef glGetString +// this should come from win32's GL/gl.h...not sure why it's not in this context +const GLubyte* WINAPI glGetString(GLenum); + +// -------------------------------------------------------------------------------- +// This part of the file contains pure functions that never access global state. +// This distinction helps keep the backend maintainable as the inputs and outputs +// of every function called in this section can be fully derived from the +// call-site alone. +// -------------------------------------------------------------------------------- + +// Prevent any code in this part of the file from accessing the global CORE state +#define CORE DONT_USE_CORE_HERE + +static size_t AToWLen(const char* a) +{ + int size_needed = MultiByteToWideChar(CP_UTF8, 0, a, -1, NULL, 0); + if (size_needed < 0) { + TRACELOG(LOG_ERROR, "failed to calculate wide length, result=%d, error=%u", size_needed, GetLastError()); + abort(); + } + return size_needed; +} +static void AToWCopy(wchar_t* out_w, size_t out_w_len, const char* a) +{ + int size = MultiByteToWideChar(CP_UTF8, 0, a, -1, out_w, out_w_len); + if (size != out_w_len) { + TRACELOG(LOG_ERROR, "expected to convert %zu utf8 chars to wide but converted %zu", out_w_len, size); + abort(); + } +} +#define AToWAlloca(out_wstr, in_a) do { \ + size_t len = AToWLen(in_a); \ + out_wstr = (WCHAR*)alloca(sizeof(WCHAR) * (len + 1)); \ + AToWCopy(out_wstr, len, in_a); \ + out_wstr[len] = 0; \ +} while (0) + +static void LogFail(const char *what, DWORD error) +{ + TRACELOG(LOG_ERROR, "%s failed, error=%lu", what, error); +} +static void LogFailHr(const char *what, HRESULT hr) +{ + TRACELOG(LOG_ERROR, "%s failed, hresult=0x%lx", what, (DWORD)hr); +} + +#define STYLE_FLAGS_RESIZABLE WS_THICKFRAME + +#define STYLE_FLAGS_UNDECORATED_OFF (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX) +#define STYLE_FLAGS_UNDECORATED_ON WS_POPUP + +static bool ResizableFromStyle(DWORD style) +{ + return 0 != (style & STYLE_FLAGS_RESIZABLE); +} +static bool DecoratedFromStyle(DWORD style) +{ + bool is_off; + if (style & STYLE_FLAGS_UNDECORATED_ON) { + if (style & STYLE_FLAGS_UNDECORATED_OFF) { + TRACELOG(LOG_ERROR, "style 0x%x has both undecorated on/off flags", style); + abort(); + } + return false; // not decorated + } + DWORD masked = (style & STYLE_FLAGS_UNDECORATED_OFF); + if (STYLE_FLAGS_UNDECORATED_OFF != masked) { + TRACELOG(LOG_ERROR, "style 0x%x is missing these flags 0x%x", masked, masked ^ STYLE_FLAGS_UNDECORATED_OFF); + abort(); + } + return true; // decorated +} +static bool HiddenFromStyle(DWORD style) +{ + return 0 == (style & WS_VISIBLE); +} + +typedef enum { MIZED_NONE, MIZED_MIN, MIZED_MAX } Mized; +Mized MizedFromStyle(DWORD style) +{ + // minimized takes precedence over maximized + if (style & WS_MINIMIZE) return MIZED_MIN; + if (style & WS_MAXIMIZE) return MIZED_MAX; + return MIZED_NONE; +} +Mized MizedFromFlags(unsigned flags) +{ + // minimized takes precedence over maximized + if (flags & FLAG_WINDOW_MINIMIZED) return MIZED_MIN; + if (flags & FLAG_WINDOW_MAXIMIZED) return MIZED_MAX; + return MIZED_NONE; +} + +static DWORD MakeWindowStyle(unsigned flags) +{ + DWORD style = + // we don't need this since we don't have any child windows, but I guess + // it improves efficiency, plus, windows adds this flag automatically anyway + // so it keeps our flags in sync with the OS. + WS_CLIPSIBLINGS + ; + style |= (flags & FLAG_WINDOW_HIDDEN) ? 0 : WS_VISIBLE; + style |= (flags & FLAG_WINDOW_RESIZABLE) ? STYLE_FLAGS_RESIZABLE : 0; + style |= (flags & FLAG_WINDOW_UNDECORATED) ? STYLE_FLAGS_UNDECORATED_ON : STYLE_FLAGS_UNDECORATED_OFF; + + switch (MizedFromFlags(flags)) { + case MIZED_NONE: break; + case MIZED_MIN: style |= WS_MINIMIZE; break; + case MIZED_MAX: style |= WS_MAXIMIZE; break; + default: abort(); + } + + // sanity checks, maybe remove later + if (ResizableFromStyle(style) != !!(flags & FLAG_WINDOW_RESIZABLE)) abort(); + if (DecoratedFromStyle(style) != !(flags & FLAG_WINDOW_UNDECORATED)) abort(); + if (HiddenFromStyle(style) != !!(flags & FLAG_WINDOW_HIDDEN)) abort(); + if (MizedFromStyle(style) != MizedFromFlags(flags)) abort(); + + return style; +} + +static bool IsMinimized2(HWND hwnd) +{ + bool is_iconic = IsIconic(hwnd); + bool style_minimized = !!(WS_MINIMIZE & GetWindowLongPtrW(hwnd, GWL_STYLE)); + if (is_iconic != style_minimized) { + TRACELOG(LOG_ERROR, "IsIconic(%d) != WS_MINIMIZED(%d)", is_iconic, style_minimized); + abort(); + } + return is_iconic; +} + +#define STYLE_MASK_ALL 0xffffffff +#define STYLE_MASK_READONLY (WS_MINIMIZE | WS_MAXIMIZE) +#define STYLE_MASK_WRITABLE (~STYLE_MASK_READONLY) + +// Enforces that the actual window/platform state is in sync with raylib's flags +static void CheckFlags( + const char* context, + HWND hwnd, + DWORD flags, + DWORD expected_style, + DWORD style_check_mask +) { + //TRACELOG(LOG_INFO, "Verifying Flags 0x%x Style 0x%x Mask 0x%x", flags, expected_style & style_check_mask, style_check_mask); + + { + DWORD style_from_flags = MakeWindowStyle(flags); + if ((style_from_flags & style_check_mask) != (expected_style & style_check_mask)) { + TRACELOG( + LOG_ERROR, + "%s: window flags (0x%x) produced style 0x%x which != expected 0x%x (diff=0x%x, mask=0x%x)", + context, + flags, + style_from_flags & style_check_mask, + expected_style & style_check_mask, + (style_from_flags & style_check_mask) ^ (expected_style & style_check_mask), + style_check_mask + ); + abort(); + } + } + + SetLastError(0); + LONG actual_style = GetWindowLongPtrW(hwnd, GWL_STYLE); + if ((actual_style & style_check_mask) != (expected_style & style_check_mask)) { + TRACELOG( + LOG_ERROR, + "%s: expected style 0x%x but got 0x%x (diff=0x%x, mask=0x%x, lasterror=%lu)", + context, + expected_style & style_check_mask, + actual_style & style_check_mask, + (expected_style & style_check_mask) ^ (actual_style & style_check_mask), + style_check_mask, + GetLastError() + ); + abort(); + } + + if (style_check_mask & WS_MINIMIZE) { + bool is_iconic = IsIconic(hwnd); + bool style_minimized = !!(WS_MINIMIZE & actual_style); + if (is_iconic != style_minimized) { + TRACELOG(LOG_ERROR, "IsIconic(%d) != WS_MINIMIZED(%d)", is_iconic, style_minimized); + abort(); + } + } + + if (style_check_mask & WS_MAXIMIZE) { + WINDOWPLACEMENT placement; + placement.length = sizeof(placement); + if (!GetWindowPlacement(hwnd, &placement)) { + LogFail("GetWindowPlacement", GetLastError()); + abort(); + } + bool placement_maximized = (placement.showCmd == SW_SHOWMAXIMIZED); + bool style_maximized = WS_MAXIMIZE & actual_style; + if (placement_maximized != style_maximized) { + TRACELOG( + LOG_ERROR, + "maximized state desync, placement maximized=%d (showCmd=%lu) style maximized=%d", + placement_maximized, + placement.showCmd, + style_maximized + ); + abort(); + } + } +} + +static float PtFromPx(float dpi_scale, bool highdpi_enabled, int pt) +{ + return highdpi_enabled ? (((float)pt) / dpi_scale) : pt; +} +static int PxFromPt(float dpi_scale, bool highdpi_enabled, float pt) +{ + return highdpi_enabled ? roundf(pt * dpi_scale) : roundf(pt); +} +static SIZE PxFromPt2(float dpi_scale, bool highdpi_enabled, Vector2 screen_size) +{ + return (SIZE){ + PxFromPt(dpi_scale, highdpi_enabled, screen_size.x), + PxFromPt(dpi_scale, highdpi_enabled, screen_size.y), + }; +} + +static SIZE GetClientSize(HWND hwnd) +{ + RECT rect; + if (0 == GetClientRect(hwnd, &rect)) { + LogFail("GetClientRect", GetLastError()); + abort(); + } + if (rect.left != 0) abort(); + if (rect.top != 0) abort(); + return (SIZE){ rect.right, rect.bottom }; +} + +static UINT GetWindowDpi(HWND hwnd) +{ + UINT dpi = GetDpiForWindow(hwnd); + if (dpi == 0) { + LogFail("GetWindowDpi", GetLastError()); + abort(); + } + return dpi; +} + +static float ScaleFromDpi(UINT dpi) +{ + return ((float)dpi) / 96.0f; +} + +#define WINDOW_STYLE_EX 0 + +static SIZE CalcWindowSize(UINT dpi, SIZE client_size, DWORD style) +{ + RECT rect = { 0, 0, client_size.cx, client_size.cy }; + if (!AdjustWindowRectExForDpi(&rect, style, 0, WINDOW_STYLE_EX, dpi)) { + LogFail("AdjustWindowRect", GetLastError()); + abort(); + } + return (SIZE){ rect.right - rect.left, rect.bottom - rect.top }; +} + +typedef enum { + UPDATE_WINDOW_FIRST, + UPDATE_WINDOW_NORMAL, +} UpdateWindowKind; + +// returns true if the window size was updated, false otherwise +static bool UpdateWindowSize( + UpdateWindowKind kind, + HWND hwnd, + Vector2 app_screen_size, + unsigned flags +) { + if (flags & FLAG_WINDOW_MINIMIZED) return false; + if (flags & FLAG_WINDOW_MAXIMIZED) { + CheckFlags("UpdateWindowSize(maximized)", hwnd, flags, MakeWindowStyle(flags), STYLE_MASK_ALL); + return false; + } + if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { + HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO info; + info.cbSize = sizeof(info); + if (!GetMonitorInfoW(monitor, &info)) { + LogFail("GetMonitorInfo", GetLastError()); + abort(); + } + RECT window_rect; + if (!GetWindowRect(hwnd, &window_rect)) { + LogFail("GetWindowRect", GetLastError()); + abort(); + } + if ( + (window_rect.left == info.rcMonitor.left) && + (window_rect.top == info.rcMonitor.top) && + ((window_rect.right - window_rect.left) == (info.rcMonitor.right - info.rcMonitor.left)) && + ((window_rect.bottom - window_rect.top) == (info.rcMonitor.bottom - info.rcMonitor.top)) + ) return false; + if (!SetWindowPos( + hwnd, + HWND_TOP, + info.rcMonitor.left, info.rcMonitor.top, + info.rcMonitor.right - info.rcMonitor.left, + info.rcMonitor.bottom - info.rcMonitor.top, + SWP_NOOWNERZORDER + )) { + LogFail("SetWindowPos", GetLastError()); + abort(); + } + return true; + } + + UINT dpi = GetWindowDpi(hwnd); + float dpi_scale = ScaleFromDpi(dpi); + bool dpi_scaling = flags & FLAG_WINDOW_HIGHDPI; + SIZE desired = PxFromPt2(dpi_scale, dpi_scaling, app_screen_size); + SIZE actual = GetClientSize(hwnd); + if (actual.cx == desired.cx || actual.cy == desired.cy) + return false; + + TRACELOG( + LOG_INFO, + "restoring client size from %dx%d to %dx%d (dpi=%lu dpi_scaling=%d app=%fx%f)", + actual.cx, actual.cy, + desired.cx, desired.cy, + dpi, dpi_scaling, + app_screen_size.x, app_screen_size.y + ); + SIZE window_size = CalcWindowSize(dpi, desired, MakeWindowStyle(flags)); + POINT window_pos = (POINT){ 0, 0 }; + UINT swp_flags = SWP_NOZORDER | SWP_FRAMECHANGED; + if (kind == UPDATE_WINDOW_FIRST) { + HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + if (!monitor) { + LogFail("MonitorFromWindow", GetLastError()); + abort(); + } + MONITORINFO info; + info.cbSize = sizeof(info); + if (!GetMonitorInfoW(monitor, &info)) { + LogFail("GetMonitorInfo", GetLastError()); + abort(); + } + LONG monitor_width = info.rcMonitor.right - info.rcMonitor.left; + LONG monitor_height = info.rcMonitor.bottom - info.rcMonitor.top; + window_pos = (POINT){ + MAX(0, (monitor_width - window_size.cx)/2), + MAX(0, (monitor_height - window_size.cy)/2), + }; + } else { + swp_flags |= SWP_NOMOVE; + } + if (!SetWindowPos( + hwnd, NULL, + window_pos.x, window_pos.y, + window_size.cx, window_size.cy, + swp_flags + )) { + LogFail("SetWindowPos", GetLastError()); + abort(); + } + return true; +} + +#define CLASS_NAME L"RaylibWindow" + +static void CreateWindowAlloca(const char* title, DWORD style) +{ + WCHAR* title_wide; + AToWAlloca(title_wide, title); + CreateWindowExW( + WINDOW_STYLE_EX, + CLASS_NAME, + title_wide, + style, + 0, 0, + 0, 0, + NULL, + NULL, + GetModuleHandleW(NULL), + NULL + ); +} + +static BOOL IsWindows10Version1703OrGreaterWin32(void) +{ + HMODULE ntdll = LoadLibraryW(L"ntdll.dll"); + DWORD (*Verify)( + RTL_OSVERSIONINFOEXW*, ULONG, ULONGLONG + ) = (DWORD (*)( + RTL_OSVERSIONINFOEXW*, ULONG, ULONGLONG + ))GetProcAddress(ntdll, "RtlVerifyVersionInfo"); + if (!Verify) { + LogFail("GetProcAddress 'RtlVerifyVersionInfo'", GetLastError()); + return 0; + } + OSVERSIONINFOEX osvi = { 0 }; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + osvi.dwMajorVersion = 10; + osvi.dwMinorVersion = 0; + osvi.dwBuildNumber = 15063; // Build 15063 corresponds to Windows 10 version 1703 (Creators Update) + DWORDLONG cond = 0; + VER_SET_CONDITION(cond, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(cond, VER_MINORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL); + return 0 == (*Verify)(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, cond); +} + +static void* FindProc(const char* name) +{ + if (0 == strcmp(name, "glGetString")) return glGetString; + return wglGetProcAddress(name); +} +static void* WglGetProcAddress(const char* name) +{ + void* result = FindProc(name); + if (result) { + //TRACELOG(LOG_DEBUG, "GetProcAddress '%s' > %p", name, result); + } else { + TRACELOG(LOG_ERROR, "GetProcAddress '%s' > %p failed, error=%u", name, result, GetLastError()); + } + return result; +} + +static KeyboardKey KeyFromWparam(WPARAM wparam) +{ + switch (wparam) { + /* case VK_LBUTTON: return KEY_; */ + /* case VK_RBUTTON: return KEY_; */ + /* case VK_CANCEL: return KEY_; */ + /* case VK_MBUTTON: return KEY_; */ + /* case VK_XBUTTON1: return KEY_; */ + /* case VK_XBUTTON2: return KEY_; */ + /* case VK_BACK: return KEY_; */ + /* case VK_TAB: return KEY_; */ + /* case VK_CLEAR: return KEY_; */ + case VK_RETURN: return KEY_ENTER; + /* case VK_SHIFT: return KEY_; */ + /* case VK_CONTROL: return KEY_; */ + /* case VK_MENU: return KEY_; */ + /* case VK_PAUSE: return KEY_; */ + /* case VK_CAPITAL: return KEY_; */ + /* case VK_KANA: return KEY_; */ + /* case VK_HANGUL: return KEY_; */ + /* case VK_IME_ON: return KEY_; */ + /* case VK_JUNJA: return KEY_; */ + /* case VK_FINAL: return KEY_; */ + /* case VK_HANJA: return KEY_; */ + /* case VK_KANJI: return KEY_; */ + /* case VK_IME_OFF: return KEY_; */ + case VK_ESCAPE: return KEY_ESCAPE; + /* case VK_CONVERT: return KEY_; */ + /* case VK_NONCONVERT: return KEY_; */ + /* case VK_ACCEPT: return KEY_; */ + /* case VK_MODECHANGE: return KEY_; */ + case VK_SPACE: return KEY_SPACE; + /* case VK_PRIOR: return KEY_; */ + /* case VK_NEXT: return KEY_; */ + /* case VK_END: return KEY_; */ + /* case VK_HOME: return KEY_; */ + case VK_LEFT: return KEY_LEFT; + case VK_UP: return KEY_UP; + case VK_RIGHT: return KEY_RIGHT; + case VK_DOWN: return KEY_DOWN; + /* case VK_SELECT: return KEY_; */ + /* case VK_PRINT: return KEY_; */ + /* case VK_EXECUTE: return KEY_; */ + /* case VK_SNAPSHOT: return KEY_; */ + /* case VK_INSERT: return KEY_; */ + /* case VK_DELETE: return KEY_; */ + /* case VK_HELP: return KEY_; */ + case '0': return KEY_ZERO; + case '1': return KEY_ONE; + case '2': return KEY_TWO; + case '3': return KEY_THREE; + case '4': return KEY_FOUR; + case '5': return KEY_FIVE; + case '6': return KEY_SIX; + case '7': return KEY_SEVEN; + case '8': return KEY_EIGHT; + case '9': return KEY_NINE; + /* case 0x3A-40: return KEY_; */ + case 'A': return KEY_A; + case 'B': return KEY_B; + case 'C': return KEY_C; + case 'D': return KEY_D; + case 'E': return KEY_E; + case 'F': return KEY_F; + case 'G': return KEY_G; + case 'H': return KEY_H; + case 'I': return KEY_I; + case 'J': return KEY_J; + case 'K': return KEY_K; + case 'L': return KEY_L; + case 'M': return KEY_M; + case 'N': return KEY_N; + case 'O': return KEY_O; + case 'P': return KEY_P; + case 'Q': return KEY_Q; + case 'R': return KEY_R; + case 'S': return KEY_S; + case 'T': return KEY_T; + case 'U': return KEY_U; + case 'V': return KEY_V; + case 'W': return KEY_W; + case 'X': return KEY_X; + case 'Y': return KEY_Y; + case 'Z': return KEY_Z; + /* case VK_LWIN: return KEY_; */ + /* case VK_RWIN: return KEY_; */ + /* case VK_APPS: return KEY_; */ + /* case VK_SLEEP: return KEY_; */ + /* case VK_NUMPAD0: return KEY_; */ + /* case VK_NUMPAD1: return KEY_; */ + /* case VK_NUMPAD2: return KEY_; */ + /* case VK_NUMPAD3: return KEY_; */ + /* case VK_NUMPAD4: return KEY_; */ + /* case VK_NUMPAD5: return KEY_; */ + /* case VK_NUMPAD6: return KEY_; */ + /* case VK_NUMPAD7: return KEY_; */ + /* case VK_NUMPAD8: return KEY_; */ + /* case VK_NUMPAD9: return KEY_; */ + /* case VK_MULTIPLY: return KEY_; */ + /* case VK_ADD: return KEY_; */ + /* case VK_SEPARATOR: return KEY_; */ + /* case VK_SUBTRACT: return KEY_; */ + /* case VK_DECIMAL: return KEY_; */ + /* case VK_DIVIDE: return KEY_; */ + /* case VK_F1: return KEY_; */ + /* case VK_F2: return KEY_; */ + /* case VK_F3: return KEY_; */ + /* case VK_F4: return KEY_; */ + /* case VK_F5: return KEY_; */ + /* case VK_F6: return KEY_; */ + /* case VK_F7: return KEY_; */ + /* case VK_F8: return KEY_; */ + /* case VK_F9: return KEY_; */ + /* case VK_F10: return KEY_; */ + /* case VK_F11: return KEY_; */ + /* case VK_F12: return KEY_; */ + /* case VK_F13: return KEY_; */ + /* case VK_F14: return KEY_; */ + /* case VK_F15: return KEY_; */ + /* case VK_F16: return KEY_; */ + /* case VK_F17: return KEY_; */ + /* case VK_F18: return KEY_; */ + /* case VK_F19: return KEY_; */ + /* case VK_F20: return KEY_; */ + /* case VK_F21: return KEY_; */ + /* case VK_F22: return KEY_; */ + /* case VK_F23: return KEY_; */ + /* case VK_F24: return KEY_; */ + /* case VK_NUMLOCK: return KEY_; */ + /* case VK_SCROLL: return KEY_; */ + /* case VK_LSHIFT: return KEY_; */ + /* case VK_RSHIFT: return KEY_; */ + /* case VK_LCONTROL: return KEY_; */ + /* case VK_RCONTROL: return KEY_; */ + /* case VK_LMENU: return KEY_; */ + /* case VK_RMENU: return KEY_; */ + /* case VK_BROWSER_BACK: return KEY_; */ + /* case VK_BROWSER_FORWARD: return KEY_; */ + /* case VK_BROWSER_REFRESH: return KEY_; */ + /* case VK_BROWSER_STOP: return KEY_; */ + /* case VK_BROWSER_SEARCH: return KEY_; */ + /* case VK_BROWSER_FAVORITES: return KEY_; */ + /* case VK_BROWSER_HOME: return KEY_; */ + /* case VK_VOLUME_MUTE: return KEY_; */ + /* case VK_VOLUME_DOWN: return KEY_; */ + /* case VK_VOLUME_UP: return KEY_; */ + /* case VK_MEDIA_NEXT_TRACK: return KEY_; */ + /* case VK_MEDIA_PREV_TRACK: return KEY_; */ + /* case VK_MEDIA_STOP: return KEY_; */ + /* case VK_MEDIA_PLAY_PAUSE: return KEY_; */ + /* case VK_LAUNCH_MAIL: return KEY_; */ + /* case VK_LAUNCH_MEDIA_SELECT: return KEY_; */ + /* case VK_LAUNCH_APP1: return KEY_; */ + /* case VK_LAUNCH_APP2: return KEY_; */ + /* case VK_OEM_1: return KEY_; */ + /* case VK_OEM_PLUS: return KEY_; */ + /* case VK_OEM_COMMA: return KEY_; */ + /* case VK_OEM_MINUS: return KEY_; */ + /* case VK_OEM_PERIOD: return KEY_; */ + /* case VK_OEM_2: return KEY_; */ + /* case VK_OEM_3: return KEY_; */ + /* case VK_OEM_4: return KEY_; */ + /* case VK_OEM_5: return KEY_; */ + /* case VK_OEM_6: return KEY_; */ + /* case VK_OEM_7: return KEY_; */ + /* case VK_OEM_8: return KEY_; */ + /* case VK_OEM_102: return KEY_; */ + /* case VK_PROCESSKEY: return KEY_; */ + /* case VK_PACKET: return KEY_; */ + /* case VK_ATTN: return KEY_; */ + /* case VK_CRSEL: return KEY_; */ + /* case VK_EXSEL: return KEY_; */ + /* case VK_EREOF: return KEY_; */ + /* case VK_PLAY: return KEY_; */ + /* case VK_ZOOM: return KEY_; */ + /* case VK_NONAME: return KEY_; */ + /* case VK_PA1: return KEY_; */ + /* case VK_OEM_CLEAR: return KEY_; */ + default: return KEY_NULL; + } +} + +static LPCWSTR GetCursorName(int cursor) +{ + switch (cursor) { + case MOUSE_CURSOR_DEFAULT : return IDC_ARROW; + case MOUSE_CURSOR_ARROW : return IDC_ARROW; + case MOUSE_CURSOR_IBEAM : return IDC_IBEAM; + case MOUSE_CURSOR_CROSSHAIR : return IDC_CROSS; + case MOUSE_CURSOR_POINTING_HAND: return IDC_HAND; + case MOUSE_CURSOR_RESIZE_EW : return IDC_SIZEWE; + case MOUSE_CURSOR_RESIZE_NS : return IDC_SIZENS; + case MOUSE_CURSOR_RESIZE_NWSE : return IDC_SIZENWSE; + case MOUSE_CURSOR_RESIZE_NESW : return IDC_SIZENESW; + case MOUSE_CURSOR_RESIZE_ALL : return IDC_SIZEALL; + case MOUSE_CURSOR_NOT_ALLOWED : return IDC_NO; + default: abort(); + } +} + + +static UINT GetMonitorDpi(HMONITOR monitor) +{ + UINT dpi_x; + UINT dpi_y; + HRESULT hr = GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y); + if (hr < 0) { + LogFailHr("GetDpiForMonitor", hr); + abort(); + } + if (dpi_x != dpi_y) { + TRACELOG(LOG_ERROR, "DPI X (%lu) != DPI Y (%lu)", dpi_x, dpi_y); + abort(); + } + if (dpi_x < 96) { + TRACELOG(LOG_ERROR, "unexpected dpi %lu", dpi_x); + abort(); + } + return dpi_x; +} + +static BOOL CALLBACK CountMonitorsProc(HMONITOR handle, HDC _, LPRECT rect, LPARAM lparam) +{ + int* count = (int*)lparam; + *count += 1; + // always return TRUE to continue the loop, otherwise, the caller + // can't distinguish between stopping the loop and an error + return TRUE; +} + +typedef struct { + HMONITOR needle; + int index; + int match_index; + RECT rect; +} FindMonitorContext; +static BOOL CALLBACK FindMonitorProc(HMONITOR handle, HDC _, LPRECT rect, LPARAM lparam) +{ + FindMonitorContext* c = (FindMonitorContext*)lparam; + if (handle == c->needle) { + c->match_index = c->index; + c->rect = *rect; + } + c->index += 1; + // always return TRUE to continue the loop, otherwise, the caller + // can't distinguish between stopping the loop and an error + return TRUE; +} + +typedef struct { + DWORD set; + DWORD clear; +} FlagsOp; + +static void GetStyleChangeFlagOps( + DWORD core_window_flags, + STYLESTRUCT* ss, + FlagsOp* deferred_flags +) { + { + bool resizable = (core_window_flags & FLAG_WINDOW_RESIZABLE); + bool resizable_old = ResizableFromStyle(ss->styleOld); + bool resizable_new = ResizableFromStyle(ss->styleNew); + if (resizable != resizable_old) { + TRACELOG(LOG_ERROR, "expected resizable %u but got %u", resizable, resizable_old); + abort(); + } + if (resizable_old != resizable_new) { + //TRACELOG(LOG_INFO, "resizable = %u", resizable_new); + if (resizable_new) { + deferred_flags->set |= FLAG_WINDOW_RESIZABLE; + } else { + deferred_flags->clear |= FLAG_WINDOW_RESIZABLE; + } + } + } + { + bool decorated = (0 == (core_window_flags & FLAG_WINDOW_UNDECORATED)); + bool decorated_old = DecoratedFromStyle(ss->styleOld); + bool decorated_new = DecoratedFromStyle(ss->styleNew); + if (decorated != decorated_old) { + TRACELOG(LOG_ERROR, "expected decorated %u but got %u", decorated, decorated_old); + abort(); + } + if (decorated_old != decorated_new) { + //TRACELOG(LOG_INFO, "decorated = %u", decorated_new); + if (decorated_new) { + deferred_flags->clear |= FLAG_WINDOW_UNDECORATED; + } else { + deferred_flags->set |= FLAG_WINDOW_UNDECORATED; + } + } + } + { + bool hidden = (core_window_flags & FLAG_WINDOW_HIDDEN); + bool hidden_old = HiddenFromStyle(ss->styleOld); + bool hidden_new = HiddenFromStyle(ss->styleNew); + if (hidden != hidden_old) { + TRACELOG(LOG_ERROR, "expected hidden %u but got %u", hidden, hidden_old); + abort(); + } + if (hidden_old != hidden_new) { + TRACELOG(LOG_INFO, "hidden = %u", hidden_new); + if (hidden_new) { + deferred_flags->set |= FLAG_WINDOW_HIDDEN; + } else { + deferred_flags->clear |= FLAG_WINDOW_HIDDEN; + } + } + } +} + +// call when the window is rezised, returns true if the new window size +// should update the desired app size +static bool AdoptWindowResize(unsigned flags) +{ + if (flags & FLAG_WINDOW_MINIMIZED) return false; + if (flags & FLAG_WINDOW_MAXIMIZED) return false; + if (flags & FLAG_FULLSCREEN_MODE) return false; + if (flags & FLAG_BORDERLESS_WINDOWED_MODE) return false; + if (!(flags & FLAG_WINDOW_RESIZABLE)) return false; + return true; +} + +// -------------------------------------------------------------------------------- +// Here's the end of the "pure function section", the rest of the file can access +// global state. +// -------------------------------------------------------------------------------- + +// unlock the ability to use CORE in the rest of the file +#undef CORE + +// The last screen size requested by the app, the backend must keep the client area +// this size (after DPI scaling is applied) when the window isn't fullscreen/maximized/minimized. +static Vector2 global_app_screen_size = (Vector2){0, 0}; +static unsigned global_desired_flags = 0; +static HWND global_hwnd = NULL; +static HDC global_hdc = NULL; +static HGLRC global_gl_context = NULL; +static LARGE_INTEGER global_timer_frequency; +static bool global_cursor_enabled = true; + +static void HandleKey(WPARAM wparam, LPARAM lparam, char state) +{ + KeyboardKey key = KeyFromWparam(wparam); + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + /* BYTE scancode = lparam >> 16; */ + /* TRACELOG(LOG_INFO, "KEY key=%d vk=%lu scan=%u = %u", key, wparam, scancode, state); */ + if (key != KEY_NULL) { + /* TRACELOG(LOG_INFO, "KEY[%d] = %d (old=%d)\n", key, state, CORE.Input.Keyboard.currentKeyState[key]); */ + CORE.Input.Keyboard.currentKeyState[key] = state; + if (key == KEY_ESCAPE && state == 1) { + CORE.Window.shouldClose = 1; + } + } else { + TRACELOG(LOG_WARNING, "unknown (or currently unhandled) virtual keycode %d (0x%x)", wparam, wparam); + } + + // TODO: add it to the queue as well? +} +static void HandleMouseButton(int button, char state) +{ + CORE.Input.Mouse.currentButtonState[button] = state; + CORE.Input.Touch.currentTouchState[button] = state; +} + +static void HandleRawInput(LPARAM lparam) +{ + RAWINPUT input; + { + UINT input_size = sizeof(input); + UINT size = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &input, &input_size, sizeof(RAWINPUTHEADER)); + if (size == (UINT)-1) { + LogFail("GetRawInputData", GetLastError()); + abort(); + } + if (size != input_size) abort(); // bug? + } + + if (input.header.dwType != RIM_TYPEMOUSE) { + TRACELOG(LOG_ERROR, "Unexpected WM_INPUT type %lu", input.header.dwType); + abort(); + } + + if (input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { + TRACELOG(LOG_ERROR, "TODO: handle absolute mouse inputs!"); + abort(); + } + if (input.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) { + TRACELOG(LOG_ERROR, "TODO: handle virtual desktop mouse inputs!"); + abort(); + } + + // bit of a trick, we keep the mouse position at 0,0 and instead move + // the previous position so we can still get a proper mouse delta + CORE.Input.Mouse.previousPosition.x -= input.data.mouse.lLastX; + CORE.Input.Mouse.previousPosition.y -= input.data.mouse.lLastY; + if (CORE.Input.Mouse.currentPosition.x != 0) abort(); + if (CORE.Input.Mouse.currentPosition.y != 0) abort(); +} + +static void HandleWindowResize(HWND hwnd, Vector2* app_screen_size_ref) +{ + if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) + return; + + SIZE client_size = GetClientSize(hwnd); + TRACELOG(LOG_DEBUG, "NewClientSize %lux%lu", client_size.cx, client_size.cy); + /* CORE.Window.currentFbo.width = client_size.cx; */ + /* CORE.Window.currentFbo.height = client_size.cy; */ + /* glViewport(0, 0, client_size.cx, client_size.cy); */ + SetupViewport(client_size.cx, client_size.cy); + CORE.Window.resizedLastFrame = true; + float dpi_scale = ScaleFromDpi(GetWindowDpi(hwnd)); + bool highdpi = !!(CORE.Window.flags & FLAG_WINDOW_HIGHDPI); + Vector2 screen_size = (Vector2){ + PtFromPx(dpi_scale, highdpi, client_size.cx), + PtFromPx(dpi_scale, highdpi, client_size.cy), + }; + CORE.Window.screen.width = roundf(screen_size.x); + CORE.Window.screen.height = roundf(screen_size.y); + if (AdoptWindowResize(CORE.Window.flags)) { + TRACELOG( + LOG_DEBUG, + "updating app size to %fx%f from window resize", + screen_size.x, screen_size.y + ); + *app_screen_size_ref = screen_size; + } + CORE.Window.screenScale = MatrixScale( + (float)CORE.Window.render.width/CORE.Window.screen.width, + (float)CORE.Window.render.height/CORE.Window.screen.height, + 1.0f + ); +} + +static void UpdateWindowStyle(HWND hwnd, unsigned desired_flags) +{ + { + DWORD current = STYLE_MASK_WRITABLE & MakeWindowStyle(CORE.Window.flags); + DWORD desired = STYLE_MASK_WRITABLE & MakeWindowStyle(desired_flags); + if (current != desired) { + SetLastError(0); + DWORD previous = STYLE_MASK_WRITABLE & SetWindowLongPtrW(hwnd, GWL_STYLE, desired); + if (previous != current) { + TRACELOG( + LOG_ERROR, + "SetWindowLong returned writable flags 0x%x but expected 0x%x (diff=0x%x, error=%lu)", + previous, + current, + previous ^ current, + GetLastError() + ); + abort(); + } + CheckFlags("UpdateWindowStyle", hwnd, desired_flags, desired, STYLE_MASK_WRITABLE); + } + } + + Mized current_mized = MizedFromStyle(MakeWindowStyle(CORE.Window.flags)); + Mized desired_mized = MizedFromStyle(MakeWindowStyle(desired_flags)); + if (current_mized != desired_mized) switch (desired_mized) { + case MIZED_NONE: ShowWindow(hwnd, SW_RESTORE); break; + case MIZED_MIN: ShowWindow(hwnd, SW_MINIMIZE); break; + case MIZED_MAX: ShowWindow(hwnd, SW_MAXIMIZE); break; + } +} + +typedef enum { + SANITIZE_FLAGS_FIRST, + SANITIZE_FLAGS_NORMAL, +} SanitizeFlagsKind; +static unsigned SanitizeFlags(SanitizeFlagsKind kind, unsigned flags) +{ + if ((flags & FLAG_WINDOW_MAXIMIZED) && (flags & FLAG_BORDERLESS_WINDOWED_MODE)) { + TRACELOG(LOG_INFO, "borderless windows mode is overriding maximized"); + flags &= ~FLAG_WINDOW_MAXIMIZED; + } + + switch (kind) { + case SANITIZE_FLAGS_FIRST: break; + case SANITIZE_FLAGS_NORMAL: break; + if ((flags & FLAG_MSAA_4X_HINT) && (!(CORE.Window.flags & FLAG_MSAA_4X_HINT))) { + TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); + flags &= ~FLAG_MSAA_4X_HINT; + } + break; + } + + return flags; +} + +#define FLAG_MASK_OPTIONAL (FLAG_VSYNC_HINT) +#define FLAG_MASK_REQUIRED ~(FLAG_MASK_OPTIONAL) + +// flags that have no operations to perform during an update +#define FLAG_MASK_NO_UPDATE (FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT) + + +// All window state changes from raylib flags go through this function. It performs +// whatever operations are needed to update the window state to match the desired flags. +// In most cases this function should not update CORE.Window.flags directly, instead, +// the window itself should update CORE.Window.flags in response to actual state changes. +// This means that CORE.Window.flags should always represent the actual state of the +// window. This function will continue to perform these update operations so long as +// the state continues to change. +// +// This design takes care of many odd corner cases. For example, if you want to restore +// a window that was previously maximized AND minimized and you want to remove both these +// flags, you actually need to call ShowWindow with SW_RESTORE twice. Another example is +// if you have a maximized window, if the undecorated flag is modified then we'd need to +// update the window style, but updating the style would mean the window size would change +// causing the window to lose its Maximized state which would mean we'd need to update the +// window size and then update the window style a second time to restore that maximized +// state. This implementation is able to handle any/all of these special situations with a +// retry loop that continues until we either reach the desired state or the state stops +// changing. +static void UpdateFlags(HWND hwnd, unsigned desired_flags, Vector2 app_screen_size) +{ + // flags that just apply immediately without needing any operations + CORE.Window.flags |= (desired_flags & FLAG_MASK_NO_UPDATE); + + { + int vsync = (CORE.Window.flags & FLAG_VSYNC_HINT) ? 1 : 0; + PFNWGLSWAPINTERVALEXTPROC f = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); + if (f) { + (*f)(vsync); + if (vsync) { + CORE.Window.flags |= FLAG_VSYNC_HINT; + } else { + CORE.Window.flags &= ~FLAG_VSYNC_HINT; + } + } + } + + DWORD previous_style; + for (unsigned attempt = 1; ; attempt++) { + CheckFlags("UpdateFlags", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), STYLE_MASK_ALL); + + bool window_size_updated = false; + if (MakeWindowStyle(CORE.Window.flags) == MakeWindowStyle(desired_flags)) { + window_size_updated = UpdateWindowSize(UPDATE_WINDOW_NORMAL, hwnd, app_screen_size, desired_flags); + if ((FLAG_MASK_REQUIRED & desired_flags) == (FLAG_MASK_REQUIRED & CORE.Window.flags)) + break; + } + + if ( + (attempt > 1) && + (previous_style == MakeWindowStyle(CORE.Window.flags)) && + !window_size_updated + ) { + TRACELOG( + LOG_ERROR, + // TODO: print more information + "UpdateFlags failed after %u attempt(s) wanted 0x%x but is 0x%x (diff=0x%x)", + attempt, + desired_flags, + CORE.Window.flags, + desired_flags ^ CORE.Window.flags + ); + abort(); + } + previous_style = MakeWindowStyle(CORE.Window.flags); + UpdateWindowStyle(hwnd, desired_flags); + } +} + +bool WindowShouldClose(void) +{ + return CORE.Window.shouldClose; +} + +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen not implemented"); + assert(0); // crash debug builds only +} + +void ToggleBorderlessWindowed(void) +{ + if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) { + ClearWindowState(FLAG_BORDERLESS_WINDOWED_MODE); + } else { + SetWindowState(FLAG_BORDERLESS_WINDOWED_MODE); + } +} + +void MaximizeWindow(void) +{ + SetWindowState(FLAG_WINDOW_MAXIMIZED); +} + +void MinimizeWindow(void) +{ + SetWindowState(FLAG_WINDOW_MINIMIZED); +} + +// Restore window from being minimized/maximized +void RestoreWindow(void) +{ + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) && + (CORE.Window.flags & FLAG_WINDOW_MINIMIZED)) { + ClearWindowState(FLAG_WINDOW_MINIMIZED); + } else { + ClearWindowState(FLAG_WINDOW_MINIMIZED|FLAG_WINDOW_MAXIMIZED); + } +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + global_desired_flags = SanitizeFlags(SANITIZE_FLAGS_NORMAL, CORE.Window.flags | flags); + UpdateFlags(global_hwnd, global_desired_flags, global_app_screen_size); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + global_desired_flags = SanitizeFlags(SANITIZE_FLAGS_NORMAL, CORE.Window.flags & ~flags); + UpdateFlags(global_hwnd, global_desired_flags, global_app_screen_size); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon not implemented"); + assert(0); // crash debug builds only +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons not implemented"); + assert(0); // crash debug builds only +} + +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; + WCHAR* titlew; + AToWAlloca(titlew, CORE.Window.title); + if (!SetWindowTextW(global_hwnd, titlew)) { + LogFail("SetWindowText", GetLastError()); + abort(); + } +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + TRACELOG(LOG_WARNING, "SetWindowPosition not implemented"); + assert(0); // crash debug builds only +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor not implemented"); + assert(0); // crash debug builds only +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowMinSize not implemented"); + assert(0); // crash debug builds only + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowMaxSize not implemented"); + assert(0); // crash debug builds only + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize not implemented"); + assert(0); // crash debug builds only +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity not implemented"); + assert(0); // crash debug builds only +} + +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused not implemented"); + assert(0); // crash debug builds only +} + +// Get native window handle +void *GetWindowHandle(void) +{ + return global_hwnd; +} + +int GetMonitorCount(void) +{ + int count = 0; + if (!EnumDisplayMonitors(NULL, NULL, CountMonitorsProc, (LPARAM)&count)) { + LogFail("EnumDisplayMonitors", GetLastError()); + abort(); + } + return count; +} + +// Get current monitor where window is placed +int GetCurrentMonitor(void) +{ + HMONITOR monitor = MonitorFromWindow(global_hwnd, MONITOR_DEFAULTTOPRIMARY); + if (!monitor) { + LogFail("MonitorFromWindow", GetLastError()); + abort(); + } + + FindMonitorContext context; + context.needle = monitor; + context.index = 0; + context.match_index = -1; + if (!EnumDisplayMonitors(NULL, NULL, FindMonitorProc, (LPARAM)&context)) { + LogFail("EnumDisplayMonitors", GetLastError()); + abort(); + } + return context.match_index; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition not implemented"); + assert(0); // crash debug builds only + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth not implemented"); + assert(0); // crash debug builds only + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight not implemented"); + assert(0); // crash debug builds only + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth not implemented"); + assert(0); // crash debug builds only + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight not implemented"); + assert(0); // crash debug builds only + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate not implemented"); + assert(0); // crash debug builds only + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorName not implemented"); + assert(0); // crash debug builds only + return 0; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition not implemented"); + assert(0); // crash debug builds only + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + float scale = ScaleFromDpi(GetWindowDpi(global_hwnd)); + return (Vector2){ scale, scale }; +} + +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText not implemented"); + assert(0); // crash debug builds only +} + +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText not implemented"); + assert(0); // crash debug builds only + return NULL; +} + +Image GetClipboardImage(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText not implemented"); + assert(0); // crash debug builds only + Image image = { 0 }; + return image; +} + +// Show mouse cursor +void ShowCursor(void) +{ + CORE.Input.Mouse.cursorHidden = false; + SetCursor(LoadCursorW(NULL, IDC_ARROW)); +} + +// Hides mouse cursor +void HideCursor(void) +{ + // NOTE: we use SetCursor instead of ShowCursor because it makes it easy + // to only hide the cursor while it's inside the client area. + CORE.Input.Mouse.cursorHidden = true; + SetCursor(NULL); +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + if (global_cursor_enabled) { + TRACELOG(LOG_INFO, "EnableCursor: already enabled"); + } else { + if (!ClipCursor(NULL)) { + LogFail("ClipCursor", GetLastError()); + abort(); + } + + { + RAWINPUTDEVICE rid; + rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC + rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE + rid.dwFlags = RIDEV_REMOVE; // Add to this window even in background + rid.hwndTarget = NULL; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + LogFail("RegisterRawInputDevices", GetLastError()); + abort(); + } + } + + ShowCursor(); + global_cursor_enabled = true; + TRACELOG(LOG_INFO, "EnableCursor: enabled"); + } +} + +// Disables cursor (lock cursor) +void DisableCursor(void) +{ + if (global_cursor_enabled) { + + { + RAWINPUTDEVICE rid; + rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC + rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE + rid.dwFlags = RIDEV_INPUTSINK; // Add to this window even in background + rid.hwndTarget = global_hwnd; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + LogFail("RegisterRawInputDevices", GetLastError()); + abort(); + } + } + + RECT client_rect; + if (!GetClientRect(global_hwnd, &client_rect)) { + LogFail("GetClientRect", GetLastError()); + abort(); + } + POINT topleft = { client_rect.left, client_rect.top }; + if (!ClientToScreen(global_hwnd, &topleft)) { + LogFail("ClientToScreen", GetLastError()); + abort(); + } + LONG width = client_rect.right - client_rect.left; + LONG height = client_rect.bottom - client_rect.top; + + TRACELOG(LOG_INFO, "ClipCursor client %d,%d %d,%d (topleft %d,%d)", + client_rect.left, + client_rect.top, + client_rect.right, + client_rect.bottom, + topleft.x, + topleft.y + ); + LONG center_x = topleft.x + width / 2; + LONG center_y = topleft.y + height / 2; + RECT clip_rect = { center_x, center_y, center_x + 1, center_y + 1 }; + if (!ClipCursor(&clip_rect)) { + LogFail("ClipCursor", GetLastError()); + abort(); + } + CORE.Input.Mouse.previousPosition = (Vector2){ 0, 0 }; + CORE.Input.Mouse.currentPosition = (Vector2){ 0, 0 }; + HideCursor(); + + global_cursor_enabled = false; + TRACELOG(LOG_INFO, "DisableCursor: disabled"); + } else { + TRACELOG(LOG_INFO, "DisableCursor: already disabled"); + } +} + +void SwapScreenBuffer(void) +{ + if (!global_hdc) abort(); + if (!wglSwapLayerBuffers(global_hdc, WGL_SWAP_MAIN_PLANE)) { + LogFail("wglSwapLayerBuffers", GetLastError()); + abort(); + } +} + +double GetTime(void) +{ + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return (double)(now.QuadPart - CORE.Time.base) / (double)global_timer_frequency.QuadPart; +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code on target platform + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + TRACELOG(LOG_WARNING, "OpenURL not implemented"); + assert(0); // crash debug builds only + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings not implemented"); + assert(0); // crash debug builds only + return -1; +} + +void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration) +{ + TRACELOG(LOG_WARNING, "SetGamepadVibration not implemented"); + assert(0); // crash debug builds only +} + +void SetMousePosition(int x, int y) +{ + if (global_cursor_enabled) { + CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + TRACELOG(LOG_WARNING, "SetMousePosition not implemented"); + assert(0); // crash debug builds only + } else { + TRACELOG(LOG_ERROR, "Possible? Should we allow this?"); + abort(); + } +} + +void SetMouseCursor(int cursor) +{ + LPCWSTR cursor_name = GetCursorName(cursor); + HCURSOR hcursor = LoadCursorW(NULL, cursor_name); + if (!hcursor) { + TRACELOG(LOG_ERROR, "LoadCursor %d (win32 %d) failed, error=%lu", cursor, (size_t)cursor_name, GetLastError()); + abort(); + } + SetCursor(hcursor); + CORE.Input.Mouse.cursorHidden = false; +} + +const char *GetKeyName(int key) +{ + TRACELOG(LOG_WARNING, "GetKeyName not implemented"); + assert(0); // crash debug builds only + return NULL; +} + +// Register all input events +void PollInputEvents(void) +{ + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + //CORE.Input.Gamepad.axisCount = 0; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on target platform the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + memcpy( + CORE.Input.Keyboard.previousKeyState, + CORE.Input.Keyboard.currentKeyState, + sizeof(CORE.Input.Keyboard.previousKeyState) + ); + memset(CORE.Input.Keyboard.keyRepeatInFrame, 0, sizeof(CORE.Input.Keyboard.keyRepeatInFrame)); + + // Register previous mouse wheel state + CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; + CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; + + // Register previous mouse position + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + + MSG msg; + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +#define WM_APP_UPDATE_WINDOW_SIZE (WM_APP+1) + +static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, FlagsOp* deferred_flags) +{ + switch (msg) { + case WM_CREATE: { + global_hdc = GetDC(hwnd); + if (!global_hdc) { + LogFail("GetDC", GetLastError()); + return -1; + } + + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) { + TRACELOG(LOG_ERROR, "TODO: implement FLAG_MSAA_4X_HINT"); + abort(); + } + + PIXELFORMATDESCRIPTOR pixelFormatDesc = {0}; + pixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pixelFormatDesc.nVersion = 1; + pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + pixelFormatDesc.iPixelType = PFD_TYPE_RGBA; + pixelFormatDesc.cColorBits = 32; + pixelFormatDesc.cAlphaBits = 8; + pixelFormatDesc.cDepthBits = 24; + + int pixelFormat = ChoosePixelFormat(global_hdc, &pixelFormatDesc); + if (!pixelFormat) { + LogFail("ChoosePixelFormat", GetLastError()); + return -1; + } + if (!SetPixelFormat(global_hdc, pixelFormat, &pixelFormatDesc)) { + LogFail("SetPixelFormat", GetLastError()); + return -1; + } + global_gl_context = wglCreateContext(global_hdc); + if (!global_gl_context) { + LogFail("wglCreateContext", GetLastError()); + return -1; + } + if (!wglMakeCurrent(global_hdc, global_gl_context)) { + LogFail("wglMakeCurrent", GetLastError()); + return -1; + } + + rlLoadExtensions(WglGetProcAddress); + + global_hwnd = hwnd; + return 0; + } + case WM_DESTROY: + wglMakeCurrent(global_hdc, NULL); + if (global_gl_context) { + if (!wglDeleteContext(global_gl_context)) abort(); + global_gl_context = NULL; + } + if (global_hdc) { + if (!ReleaseDC(hwnd, global_hdc)) abort(); + global_hdc = NULL; + } + return 0; + case WM_CLOSE: + CORE.Window.shouldClose = true; + return 0; + case WM_KILLFOCUS: + memset(CORE.Input.Keyboard.previousKeyState, 0, sizeof(CORE.Input.Keyboard.previousKeyState)); + memset(CORE.Input.Keyboard.currentKeyState, 0, sizeof(CORE.Input.Keyboard.currentKeyState)); + return 0; + case WM_SIZING: { + if (CORE.Window.flags & FLAG_WINDOW_RESIZABLE) { + // TODO: enforce min/max size + TRACELOG(LOG_WARNING, "TODO: enforce min/max size!"); + } else { + TRACELOG(LOG_WARNING, "non-resizable window is being resized"); + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + abort(); // TODO + } + return TRUE; + } + case WM_STYLECHANGING: + if (wparam == GWL_STYLE) { + STYLESTRUCT* ss = (STYLESTRUCT*)lparam; + GetStyleChangeFlagOps(CORE.Window.flags, ss, deferred_flags); + + UINT dpi = GetWindowDpi(hwnd); + SIZE client_size = GetClientSize(hwnd); + SIZE old_size = CalcWindowSize(dpi, client_size, ss->styleOld); + SIZE new_size = CalcWindowSize(dpi, client_size, ss->styleNew); + if (old_size.cx != new_size.cx || old_size.cy != new_size.cy) { + TRACELOG( + LOG_INFO, + "resize from style change: %dx%d to %dx%d", + old_size.cx, old_size.cy, + new_size.cx, new_size.cy + ); + if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) { + // looks like windows will automatically "unminimize" a window + // if a style changes modifies it's size + TRACELOG(LOG_INFO, "style change modifed window size, removing maximized flag"); + deferred_flags->clear |= FLAG_WINDOW_MAXIMIZED; + } + } + } + return 0; + case WM_WINDOWPOSCHANGING: { + WINDOWPOS* pos = (WINDOWPOS*)lparam; + if (pos->flags & SWP_SHOWWINDOW) { + if (pos->flags & SWP_HIDEWINDOW) abort(); + deferred_flags->clear |= FLAG_WINDOW_HIDDEN; + } else if (pos->flags & SWP_HIDEWINDOW) { + deferred_flags->set |= FLAG_WINDOW_HIDDEN; + } + + Mized mized = MIZED_NONE; + if (IsMinimized2(hwnd)) { + mized = MIZED_MIN; + } else { + WINDOWPLACEMENT placement; + placement.length = sizeof(placement); + if (!GetWindowPlacement(hwnd, &placement)) { + LogFail("GetWindowPlacement", GetLastError()); + abort(); + } + if (placement.showCmd == SW_SHOWMAXIMIZED) { + mized = MIZED_MAX; + } + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + /* TRACELOG(LOG_INFO, "window pos=%d,%d size=%dx%d", pos->x, pos->y, pos->cx, pos->cy); */ + + switch (mized) { + case MIZED_NONE: + deferred_flags->clear |= ( + FLAG_WINDOW_MINIMIZED | + FLAG_WINDOW_MAXIMIZED + ); + HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO info; + info.cbSize = sizeof(info); + if (!GetMonitorInfoW(monitor, &info)) { + LogFail("GetMonitorInfo", GetLastError()); + abort(); + } + if ( + (pos->x == info.rcMonitor.left) && + (pos->y == info.rcMonitor.top) && + (pos->cx == (info.rcMonitor.right - info.rcMonitor.left)) && + (pos->cy == (info.rcMonitor.bottom - info.rcMonitor.top)) + ) { + deferred_flags->set |= FLAG_BORDERLESS_WINDOWED_MODE; + } else { + deferred_flags->clear |= FLAG_BORDERLESS_WINDOWED_MODE; + } + break; + case MIZED_MIN: + // !!! NOTE !!! Do not update the maximized/borderless + // flags because when hwnd is minimized it temporarily overrides + // the maximized state/flag which gets restored on SW_RESTORE + deferred_flags->set |= FLAG_WINDOW_MINIMIZED; + break; + case MIZED_MAX: + deferred_flags->clear |= FLAG_WINDOW_MINIMIZED; + deferred_flags->set |= FLAG_WINDOW_MAXIMIZED; + break; + } + + return 0; + } + case WM_SIZE: + // don't trust the docs, they say you won't get this message if you don't call DefWindowProc + // in response to WM_WINDOWPOSCHANGED but looks like when a window is created you'll get this + // message without getting WM_WINDOWPOSCHANGED. + HandleWindowResize(hwnd, &global_app_screen_size); + return 0; + case WM_WINDOWPOSCHANGED: { + WINDOWPOS* pos = (WINDOWPOS*)lparam; + if (!(pos->flags & SWP_NOSIZE)) { + HandleWindowResize(hwnd, &global_app_screen_size); + } + return 0; + } + case WM_GETDPISCALEDSIZE: { + SIZE *inout_size = (SIZE*)lparam; + UINT new_dpi = wparam; + + // for any of these other cases, we might want to post a window + // resize event after the dpi changes? + if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return TRUE; + if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) return TRUE; + if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) return TRUE; + + float dpi_scale = ScaleFromDpi(new_dpi); + bool dpi_scaling = CORE.Window.flags & FLAG_WINDOW_HIGHDPI; + SIZE desired = PxFromPt2(dpi_scale, dpi_scaling, global_app_screen_size); + inout_size->cx = desired.cx; + inout_size->cy = desired.cy; + return TRUE; + } + case WM_DPICHANGED: { + RECT *suggested_rect = (RECT*)lparam; + // Never set the window size to anything other than the suggested rect here. + // Doing so can cause a window to stutter between monitors when transitioning + // between them. + if (!SetWindowPos( + hwnd, + NULL, + suggested_rect->left, + suggested_rect->top, + suggested_rect->right - suggested_rect->left, + suggested_rect->bottom - suggested_rect->top, + SWP_NOZORDER | SWP_NOACTIVATE + )) { + LogFail("SetWindowPos", GetLastError()); + abort(); + } + return 0; + } + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + EndPaint(hwnd, &ps); + return 0; + } + case WM_SETCURSOR: + if (LOWORD(lparam) == HTCLIENT) { + SetCursor(CORE.Input.Mouse.cursorHidden ? NULL : LoadCursorW(NULL, IDC_ARROW)); + return 0; + } + return DefWindowProc(hwnd, msg, wparam, lparam); + case WM_INPUT: + if (global_cursor_enabled) { + TRACELOG(LOG_ERROR, "Unexpected raw mouse input"); // impossible right? + abort(); + } + HandleRawInput(lparam); + return 0; + case WM_MOUSEMOVE: + if (global_cursor_enabled) { + CORE.Input.Mouse.currentPosition.x = (float)GET_X_LPARAM(lparam); + CORE.Input.Mouse.currentPosition.y = (float)GET_Y_LPARAM(lparam); + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + } + return 0; + case WM_KEYDOWN: HandleKey(wparam, lparam, 1); return 0; + case WM_KEYUP: HandleKey(wparam, lparam, 0); return 0; + case WM_LBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_LEFT, 1); return 0; + case WM_LBUTTONUP : HandleMouseButton(MOUSE_BUTTON_LEFT, 0); return 0; + case WM_RBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_RIGHT, 1); return 0; + case WM_RBUTTONUP : HandleMouseButton(MOUSE_BUTTON_RIGHT, 0); return 0; + case WM_MBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_MIDDLE, 1); return 0; + case WM_MBUTTONUP : HandleMouseButton(MOUSE_BUTTON_MIDDLE, 0); return 0; + case WM_XBUTTONDOWN: switch (HIWORD(wparam)) { + case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 1); return 0; + case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 1); return 0; + default: + TRACELOG(LOG_WARNING, "TODO: handle ex mouse button DOWN wparam=%u", HIWORD(wparam)); + return 0; + } + case WM_XBUTTONUP: switch (HIWORD(wparam)) { + case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 0); return 0; + case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 0); return 0; + default: + TRACELOG(LOG_WARNING, "TODO: handle ex mouse button UP wparam=%u", HIWORD(wparam)); + return 0; + } + case WM_MOUSEWHEEL: + CORE.Input.Mouse.currentWheelMove.y = ((float)GET_WHEEL_DELTA_WPARAM(wparam)) / WHEEL_DELTA; + return 0; + case WM_MOUSEHWHEEL: + CORE.Input.Mouse.currentWheelMove.x = ((float)GET_WHEEL_DELTA_WPARAM(wparam)) / WHEEL_DELTA; + return 0; + case WM_APP_UPDATE_WINDOW_SIZE: + UpdateWindowSize(UPDATE_WINDOW_NORMAL, hwnd, global_app_screen_size, CORE.Window.flags); + return 0; + default: + return DefWindowProcW(hwnd, msg, wparam, lparam); + } +} +static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + // sanity check, should we remove this? + DWORD mask = STYLE_MASK_ALL; + if (global_hwnd == hwnd) { + if (msg == WM_WINDOWPOSCHANGING) { + mask &= ~(WS_MINIMIZE | WS_MAXIMIZE); + } + CheckFlags("WndProc", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), mask); + } + + FlagsOp flags_op; + flags_op.set = 0; + flags_op.clear = 0; + LRESULT result = WndProc2(hwnd, msg, wparam, lparam, &flags_op); + + // sanity check, should we remove this? + if (global_hwnd == hwnd) { + CheckFlags("After WndProc", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), mask); + } + + // Operations to execute after the above check + if (flags_op.set & flags_op.clear) { + TRACELOG(LOG_ERROR, "the flags 0x%x were both set and cleared!", flags_op.set & flags_op.clear); + abort(); + } + { + DWORD save = CORE.Window.flags; + CORE.Window.flags |= flags_op.set; + CORE.Window.flags &= ~flags_op.clear; + if (save != CORE.Window.flags) { + TRACELOG( + LOG_DEBUG, + "DeferredFlags: 0x%x > 0x%x (diff 0x%x)", + save, + CORE.Window.flags, + save ^ CORE.Window.flags + ); + } + } + return result; +} + + +int InitPlatform(void) +{ + global_desired_flags = SanitizeFlags(SANITIZE_FLAGS_FIRST, CORE.Window.flags); + // from this point CORE.Window.flags should always reflect the actual state of the window + CORE.Window.flags = FLAG_WINDOW_HIDDEN | (global_desired_flags & FLAG_MASK_NO_UPDATE); + + global_app_screen_size = (Vector2){ CORE.Window.screen.width, CORE.Window.screen.height }; + + if (IsWindows10Version1703OrGreaterWin32()) { + TRACELOG(LOG_INFO, "DpiAware: >=Win10Creators"); + if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) { + LogFail("SetProcessDpiAwarenessContext", GetLastError()); + abort(); + } + } else if (IsWindows8Point1OrGreater()) { + TRACELOG(LOG_INFO, "DpiAware: >=Win8.1"); + HMODULE shcore = LoadLibraryW(L"shcore.dll"); + if (!shcore) { + LogFail("LoadLibary shcore.dll", GetLastError()); + abort(); + } + HRESULT (*Set)(int) = (HRESULT(*)(int))GetProcAddress(shcore, "SetProcessDpiAwareness"); + if (!Set) { + LogFail("GetProcessAddress SetProcessDpiAwareness", GetLastError()); + abort(); + } + HRESULT hr = (*Set)(PROCESS_PER_MONITOR_DPI_AWARE); + if (hr < 0) { + LogFailHr("SetProcessDpiAwareness", hr); + abort(); + } + } else if (IsWindowsVistaOrGreater()) { + TRACELOG(LOG_INFO, "DpiAware: >=WinVista"); + SetProcessDPIAware(); + } else { + TRACELOG(LOG_INFO, "DpiAware: Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + + CORE.Storage.basePath = GetWorkingDirectory(); + + QueryPerformanceFrequency(&global_timer_frequency); + + { + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + CORE.Time.base = time.QuadPart; + } + InitTimer(); + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: WIN32: Initialized successfully"); + return 0; +} + +void ClosePlatform(void) +{ + if (global_hwnd) { + if (0 == DestroyWindow(global_hwnd)) { + LogFail("DestroyWindow", GetLastError()); + abort(); + } + global_hwnd = NULL; + } +} diff --git a/src/rcore.c b/src/rcore.c index ea971b52f..460250cbc 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -553,6 +553,8 @@ const char *TextFormat(const char *text, ...); // Formatting of tex #include "platforms/rcore_desktop_sdl.c" #elif (defined(PLATFORM_DESKTOP_RGFW) || defined(PLATFORM_WEB_RGFW)) #include "platforms/rcore_desktop_rgfw.c" +#elif defined(PLATFORM_DESKTOP_WIN32) + #include "platforms/rcore_desktop_win32.c" #elif defined(PLATFORM_WEB) #include "platforms/rcore_web.c" #elif defined(PLATFORM_DRM) @@ -622,6 +624,8 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)"); #elif defined(PLATFORM_DESKTOP_RGFW) TRACELOG(LOG_INFO, "Platform backend: DESKTOP (RGFW)"); +#elif defined(PLATFORM_DESKTOP_WIN32) + TRACELOG(LOG_INFO, "Platform backend: DESKTOP (WIN32)"); #elif defined(PLATFORM_WEB_RGFW) TRACELOG(LOG_INFO, "Platform backend: WEB (RGFW) (HTML5)"); #elif defined(PLATFORM_WEB) From 8e28a942f93a35fbe1a93361a8cd6039bdf5ddc2 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 4 Apr 2025 09:28:27 -0600 Subject: [PATCH 02/10] [rcore][win32] use SwapBuffers instead of wglSwapLayerBuffers I don't understand OpenGL well enough to know the difference between SwapBuffers and wglSwapLayerBuffers but the former seems to double my FPS (from 2000 to about 4000 in core_vr_simulator). --- src/platforms/rcore_desktop_win32.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index ef790f655..f24f51e41 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1435,8 +1435,8 @@ void DisableCursor(void) void SwapScreenBuffer(void) { if (!global_hdc) abort(); - if (!wglSwapLayerBuffers(global_hdc, WGL_SWAP_MAIN_PLANE)) { - LogFail("wglSwapLayerBuffers", GetLastError()); + if (!SwapBuffers(global_hdc)) { + LogFail("SwapBuffers", GetLastError()); abort(); } } From 6db18524b451b98d9ed5fd24282023ed415b1e28 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 4 Apr 2025 10:50:00 -0600 Subject: [PATCH 03/10] [rcore][win32] stop lying to the OS about when our window is updated Instead of calling BeginPaint/EndPaint in WM_PAINT which signals to the OS that our window content is updated, now when we encounter the WM_PAINT message instead we return back to the raylib application which will trigger it to render a new frame. We've replaced the call to BeginPaint and EndPaint with a call to ValidateRect in SwapBuffers, which, means we're now correctly telling the OS when our window content is actually up-to-date. Note that this doesn't fix the window content not being updated during a window resize/move beacuse thos have their own message loop which doesn't return early when it's time to paint. --- src/platforms/rcore_desktop_win32.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index f24f51e41..285a8beb6 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1439,6 +1439,11 @@ void SwapScreenBuffer(void) LogFail("SwapBuffers", GetLastError()); abort(); } + + if (!ValidateRect(global_hwnd, NULL)) { + LogFail("ValidateRect", GetLastError()); + abort(); + } } double GetTime(void) @@ -1551,6 +1556,8 @@ void PollInputEvents(void) MSG msg; while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_PAINT) + return; TranslateMessage(&msg); DispatchMessageW(&msg); } @@ -1778,12 +1785,6 @@ static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, Flags } return 0; } - case WM_PAINT: { - PAINTSTRUCT ps; - BeginPaint(hwnd, &ps); - EndPaint(hwnd, &ps); - return 0; - } case WM_SETCURSOR: if (LOWORD(lparam) == HTCLIENT) { SetCursor(CORE.Input.Mouse.cursorHidden ? NULL : LoadCursorW(NULL, IDC_ARROW)); From afe367817add088d36306ad4b07d4bb2c4a6bf35 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 4 Apr 2025 11:12:36 -0600 Subject: [PATCH 04/10] [rcore][win32] fallback to finding functions from opengl32.dll --- src/platforms/rcore_desktop_win32.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 285a8beb6..9171a7d23 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -75,11 +75,6 @@ #include #include -// undefined the glad_glGetString macro definition -#undef glGetString -// this should come from win32's GL/gl.h...not sure why it's not in this context -const GLubyte* WINAPI glGetString(GLenum); - // -------------------------------------------------------------------------------- // This part of the file contains pure functions that never access global state. // This distinction helps keep the backend maintainable as the inputs and outputs @@ -490,8 +485,21 @@ static BOOL IsWindows10Version1703OrGreaterWin32(void) static void* FindProc(const char* name) { - if (0 == strcmp(name, "glGetString")) return glGetString; - return wglGetProcAddress(name); + { + void* proc = wglGetProcAddress(name); + if (proc) return proc; + } + + static HMODULE opengl = NULL; + if (!opengl) { + opengl = LoadLibraryW(L"opengl32"); + } + if (opengl) { + void* proc = GetProcAddress(opengl, name); + if (proc) return proc; + } + + return NULL; } static void* WglGetProcAddress(const char* name) { From df5a3cee4ae9bb7287297e7f25f7080b6079be36 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 18 Apr 2025 07:41:36 -0600 Subject: [PATCH 05/10] [rcore][win32] fixes from review/for gcc --- src/Makefile | 7 +++++++ src/platforms/rcore_desktop_win32.c | 23 +---------------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/src/Makefile b/src/Makefile index 833f955a9..ef0001186 100644 --- a/src/Makefile +++ b/src/Makefile @@ -342,6 +342,10 @@ ifeq ($(PLATFORM_OS), LINUX) CFLAGS += -fPIC endif +ifeq ($(PLATFORM_OS),WINDOWS) + CFLAGS += -DUNICODE +endif + ifeq ($(RAYLIB_BUILD_MODE),DEBUG) CFLAGS += -g -D_DEBUG endif @@ -628,6 +632,9 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DRM) LDLIBS += -latomic endif endif +ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32) + LDLIBS = -lgdi32 -lwinmm -lopengl32 -lshcore +endif ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID) LDLIBS = -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lc -lm endif diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 9171a7d23..e47f0cf76 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -129,7 +129,6 @@ static bool ResizableFromStyle(DWORD style) } static bool DecoratedFromStyle(DWORD style) { - bool is_off; if (style & STYLE_FLAGS_UNDECORATED_ON) { if (style & STYLE_FLAGS_UNDECORATED_OFF) { TRACELOG(LOG_ERROR, "style 0x%x has both undecorated on/off flags", style); @@ -471,7 +470,7 @@ static BOOL IsWindows10Version1703OrGreaterWin32(void) LogFail("GetProcAddress 'RtlVerifyVersionInfo'", GetLastError()); return 0; } - OSVERSIONINFOEX osvi = { 0 }; + RTL_OSVERSIONINFOEXW osvi = { 0 }; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); osvi.dwMajorVersion = 10; osvi.dwMinorVersion = 0; @@ -713,26 +712,6 @@ static LPCWSTR GetCursorName(int cursor) } -static UINT GetMonitorDpi(HMONITOR monitor) -{ - UINT dpi_x; - UINT dpi_y; - HRESULT hr = GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y); - if (hr < 0) { - LogFailHr("GetDpiForMonitor", hr); - abort(); - } - if (dpi_x != dpi_y) { - TRACELOG(LOG_ERROR, "DPI X (%lu) != DPI Y (%lu)", dpi_x, dpi_y); - abort(); - } - if (dpi_x < 96) { - TRACELOG(LOG_ERROR, "unexpected dpi %lu", dpi_x); - abort(); - } - return dpi_x; -} - static BOOL CALLBACK CountMonitorsProc(HMONITOR handle, HDC _, LPRECT rect, LPARAM lparam) { int* count = (int*)lparam; From 93dc95eb2e10a651d52d3dd64d0831c474c8f35d Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 18 Apr 2025 08:34:48 -0600 Subject: [PATCH 06/10] [rcore][win32] incorporate style conventions --- src/platforms/rcore_desktop_win32.c | 1696 +++++++++++++++------------ 1 file changed, 946 insertions(+), 750 deletions(-) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index e47f0cf76..6a7a79e5c 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -85,28 +85,31 @@ // Prevent any code in this part of the file from accessing the global CORE state #define CORE DONT_USE_CORE_HERE -static size_t AToWLen(const char* a) +static size_t AToWLen(const char *a) { - int size_needed = MultiByteToWideChar(CP_UTF8, 0, a, -1, NULL, 0); - if (size_needed < 0) { - TRACELOG(LOG_ERROR, "failed to calculate wide length, result=%d, error=%u", size_needed, GetLastError()); + int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, a, -1, NULL, 0); + if (sizeNeeded < 0) + { + TRACELOG(LOG_ERROR, "failed to calculate wide length, result=%d, error=%u", sizeNeeded, GetLastError()); abort(); } - return size_needed; + + return sizeNeeded; } -static void AToWCopy(wchar_t* out_w, size_t out_w_len, const char* a) +static void AToWCopy(wchar_t *outPtr, size_t outLen, const char *a) { - int size = MultiByteToWideChar(CP_UTF8, 0, a, -1, out_w, out_w_len); - if (size != out_w_len) { - TRACELOG(LOG_ERROR, "expected to convert %zu utf8 chars to wide but converted %zu", out_w_len, size); + int size = MultiByteToWideChar(CP_UTF8, 0, a, -1, outPtr, outLen); + if (size != outLen) + { + TRACELOG(LOG_ERROR, "expected to convert %zu utf8 chars to wide but converted %zu", outLen, size); abort(); } } -#define AToWAlloca(out_wstr, in_a) do { \ - size_t len = AToWLen(in_a); \ - out_wstr = (WCHAR*)alloca(sizeof(WCHAR) * (len + 1)); \ - AToWCopy(out_wstr, len, in_a); \ - out_wstr[len] = 0; \ +#define A_TO_W_ALLOCA(outWstr, inAnsi) do { \ + size_t len = AToWLen(inAnsi); \ + outWstr = (WCHAR*)alloca(sizeof(WCHAR)*(len + 1)); \ + AToWCopy(outWstr, len, inAnsi); \ + outWstr[len] = 0; \ } while (0) static void LogFail(const char *what, DWORD error) @@ -129,18 +132,24 @@ static bool ResizableFromStyle(DWORD style) } static bool DecoratedFromStyle(DWORD style) { - if (style & STYLE_FLAGS_UNDECORATED_ON) { - if (style & STYLE_FLAGS_UNDECORATED_OFF) { + if (style & STYLE_FLAGS_UNDECORATED_ON) + { + if (style & STYLE_FLAGS_UNDECORATED_OFF) + { TRACELOG(LOG_ERROR, "style 0x%x has both undecorated on/off flags", style); abort(); } + return false; // not decorated } + DWORD masked = (style & STYLE_FLAGS_UNDECORATED_OFF); - if (STYLE_FLAGS_UNDECORATED_OFF != masked) { + if (STYLE_FLAGS_UNDECORATED_OFF != masked) + { TRACELOG(LOG_ERROR, "style 0x%x is missing these flags 0x%x", masked, masked ^ STYLE_FLAGS_UNDECORATED_OFF); abort(); } + return true; // decorated } static bool HiddenFromStyle(DWORD style) @@ -172,15 +181,16 @@ static DWORD MakeWindowStyle(unsigned flags) // so it keeps our flags in sync with the OS. WS_CLIPSIBLINGS ; - style |= (flags & FLAG_WINDOW_HIDDEN) ? 0 : WS_VISIBLE; - style |= (flags & FLAG_WINDOW_RESIZABLE) ? STYLE_FLAGS_RESIZABLE : 0; - style |= (flags & FLAG_WINDOW_UNDECORATED) ? STYLE_FLAGS_UNDECORATED_ON : STYLE_FLAGS_UNDECORATED_OFF; + style |= (flags & FLAG_WINDOW_HIDDEN)? 0 : WS_VISIBLE; + style |= (flags & FLAG_WINDOW_RESIZABLE)? STYLE_FLAGS_RESIZABLE : 0; + style |= (flags & FLAG_WINDOW_UNDECORATED)? STYLE_FLAGS_UNDECORATED_ON : STYLE_FLAGS_UNDECORATED_OFF; - switch (MizedFromFlags(flags)) { - case MIZED_NONE: break; - case MIZED_MIN: style |= WS_MINIMIZE; break; - case MIZED_MAX: style |= WS_MAXIMIZE; break; - default: abort(); + switch (MizedFromFlags(flags)) + { + case MIZED_NONE: break; + case MIZED_MIN: style |= WS_MINIMIZE; break; + case MIZED_MAX: style |= WS_MAXIMIZE; break; + default: abort(); } // sanity checks, maybe remove later @@ -194,13 +204,15 @@ static DWORD MakeWindowStyle(unsigned flags) static bool IsMinimized2(HWND hwnd) { - bool is_iconic = IsIconic(hwnd); - bool style_minimized = !!(WS_MINIMIZE & GetWindowLongPtrW(hwnd, GWL_STYLE)); - if (is_iconic != style_minimized) { - TRACELOG(LOG_ERROR, "IsIconic(%d) != WS_MINIMIZED(%d)", is_iconic, style_minimized); + bool isIconic = IsIconic(hwnd); + bool styleMinimized = !!(WS_MINIMIZE & GetWindowLongPtrW(hwnd, GWL_STYLE)); + if (isIconic != styleMinimized) + { + TRACELOG(LOG_ERROR, "IsIconic(%d) != WS_MINIMIZED(%d)", isIconic, styleMinimized); abort(); } - return is_iconic; + + return isIconic; } #define STYLE_MASK_ALL 0xffffffff @@ -209,98 +221,104 @@ static bool IsMinimized2(HWND hwnd) // Enforces that the actual window/platform state is in sync with raylib's flags static void CheckFlags( - const char* context, + const char *context, HWND hwnd, DWORD flags, - DWORD expected_style, - DWORD style_check_mask + DWORD expectedStyle, + DWORD styleCheckMask ) { - //TRACELOG(LOG_INFO, "Verifying Flags 0x%x Style 0x%x Mask 0x%x", flags, expected_style & style_check_mask, style_check_mask); + //TRACELOG(LOG_INFO, "Verifying Flags 0x%x Style 0x%x Mask 0x%x", flags, expectedStyle & styleCheckMask, styleCheckMask); { - DWORD style_from_flags = MakeWindowStyle(flags); - if ((style_from_flags & style_check_mask) != (expected_style & style_check_mask)) { + DWORD styleFromFlags = MakeWindowStyle(flags); + if ((styleFromFlags & styleCheckMask) != (expectedStyle & styleCheckMask)) + { TRACELOG( LOG_ERROR, "%s: window flags (0x%x) produced style 0x%x which != expected 0x%x (diff=0x%x, mask=0x%x)", context, flags, - style_from_flags & style_check_mask, - expected_style & style_check_mask, - (style_from_flags & style_check_mask) ^ (expected_style & style_check_mask), - style_check_mask + styleFromFlags & styleCheckMask, + expectedStyle & styleCheckMask, + (styleFromFlags & styleCheckMask) ^ (expectedStyle & styleCheckMask), + styleCheckMask ); abort(); } } SetLastError(0); - LONG actual_style = GetWindowLongPtrW(hwnd, GWL_STYLE); - if ((actual_style & style_check_mask) != (expected_style & style_check_mask)) { + LONG actualStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + if ((actualStyle & styleCheckMask) != (expectedStyle & styleCheckMask)) + { TRACELOG( LOG_ERROR, "%s: expected style 0x%x but got 0x%x (diff=0x%x, mask=0x%x, lasterror=%lu)", context, - expected_style & style_check_mask, - actual_style & style_check_mask, - (expected_style & style_check_mask) ^ (actual_style & style_check_mask), - style_check_mask, + expectedStyle & styleCheckMask, + actualStyle & styleCheckMask, + (expectedStyle & styleCheckMask) ^ (actualStyle & styleCheckMask), + styleCheckMask, GetLastError() ); abort(); } - if (style_check_mask & WS_MINIMIZE) { - bool is_iconic = IsIconic(hwnd); - bool style_minimized = !!(WS_MINIMIZE & actual_style); - if (is_iconic != style_minimized) { - TRACELOG(LOG_ERROR, "IsIconic(%d) != WS_MINIMIZED(%d)", is_iconic, style_minimized); + if (styleCheckMask & WS_MINIMIZE) + { + bool isIconic = IsIconic(hwnd); + bool styleMinimized = !!(WS_MINIMIZE & actualStyle); + if (isIconic != styleMinimized) { + TRACELOG(LOG_ERROR, "IsIconic(%d) != WS_MINIMIZED(%d)", isIconic, styleMinimized); abort(); } } - if (style_check_mask & WS_MAXIMIZE) { + if (styleCheckMask & WS_MAXIMIZE) + { WINDOWPLACEMENT placement; placement.length = sizeof(placement); if (!GetWindowPlacement(hwnd, &placement)) { LogFail("GetWindowPlacement", GetLastError()); abort(); } - bool placement_maximized = (placement.showCmd == SW_SHOWMAXIMIZED); - bool style_maximized = WS_MAXIMIZE & actual_style; - if (placement_maximized != style_maximized) { + bool placementMaximized = (placement.showCmd == SW_SHOWMAXIMIZED); + bool styleMaximized = WS_MAXIMIZE & actualStyle; + if (placementMaximized != styleMaximized) + { TRACELOG( LOG_ERROR, "maximized state desync, placement maximized=%d (showCmd=%lu) style maximized=%d", - placement_maximized, + placementMaximized, placement.showCmd, - style_maximized + styleMaximized ); abort(); } } } -static float PtFromPx(float dpi_scale, bool highdpi_enabled, int pt) +static float PtFromPx(float dpiScale, bool highdpiEnabled, int pt) { - return highdpi_enabled ? (((float)pt) / dpi_scale) : pt; + return highdpiEnabled? (((float)pt)/dpiScale) : pt; } -static int PxFromPt(float dpi_scale, bool highdpi_enabled, float pt) +static int PxFromPt(float dpiScale, bool highdpiEnabled, float pt) { - return highdpi_enabled ? roundf(pt * dpi_scale) : roundf(pt); + return highdpiEnabled? roundf(pt*dpiScale) : roundf(pt); } -static SIZE PxFromPt2(float dpi_scale, bool highdpi_enabled, Vector2 screen_size) +static SIZE PxFromPt2(float dpiScale, bool highdpiEnabled, Vector2 screenSize) { return (SIZE){ - PxFromPt(dpi_scale, highdpi_enabled, screen_size.x), - PxFromPt(dpi_scale, highdpi_enabled, screen_size.y), + PxFromPt(dpiScale, highdpiEnabled, screenSize.x), + PxFromPt(dpiScale, highdpiEnabled, screenSize.y), }; } static SIZE GetClientSize(HWND hwnd) { RECT rect; - if (0 == GetClientRect(hwnd, &rect)) { + if (0 == GetClientRect(hwnd, &rect)) + { LogFail("GetClientRect", GetLastError()); abort(); } @@ -312,24 +330,27 @@ static SIZE GetClientSize(HWND hwnd) static UINT GetWindowDpi(HWND hwnd) { UINT dpi = GetDpiForWindow(hwnd); - if (dpi == 0) { + if (dpi == 0) + { LogFail("GetWindowDpi", GetLastError()); abort(); } + return dpi; } static float ScaleFromDpi(UINT dpi) { - return ((float)dpi) / 96.0f; + return ((float)dpi)/96.0f; } #define WINDOW_STYLE_EX 0 -static SIZE CalcWindowSize(UINT dpi, SIZE client_size, DWORD style) +static SIZE CalcWindowSize(UINT dpi, SIZE clientSize, DWORD style) { - RECT rect = { 0, 0, client_size.cx, client_size.cy }; - if (!AdjustWindowRectExForDpi(&rect, style, 0, WINDOW_STYLE_EX, dpi)) { + RECT rect = { 0, 0, clientSize.cx, clientSize.cy }; + if (!AdjustWindowRectExForDpi(&rect, style, 0, WINDOW_STYLE_EX, dpi)) + { LogFail("AdjustWindowRect", GetLastError()); abort(); } @@ -345,33 +366,42 @@ typedef enum { static bool UpdateWindowSize( UpdateWindowKind kind, HWND hwnd, - Vector2 app_screen_size, + Vector2 appScreenSize, unsigned flags ) { if (flags & FLAG_WINDOW_MINIMIZED) return false; - if (flags & FLAG_WINDOW_MAXIMIZED) { + + if (flags & FLAG_WINDOW_MAXIMIZED) + { CheckFlags("UpdateWindowSize(maximized)", hwnd, flags, MakeWindowStyle(flags), STYLE_MASK_ALL); return false; } - if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { + + if (flags & FLAG_BORDERLESS_WINDOWED_MODE) + { HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); MONITORINFO info; info.cbSize = sizeof(info); - if (!GetMonitorInfoW(monitor, &info)) { + if (!GetMonitorInfoW(monitor, &info)) + { LogFail("GetMonitorInfo", GetLastError()); abort(); } - RECT window_rect; - if (!GetWindowRect(hwnd, &window_rect)) { + + RECT windowRect; + if (!GetWindowRect(hwnd, &windowRect)) + { LogFail("GetWindowRect", GetLastError()); abort(); } + if ( - (window_rect.left == info.rcMonitor.left) && - (window_rect.top == info.rcMonitor.top) && - ((window_rect.right - window_rect.left) == (info.rcMonitor.right - info.rcMonitor.left)) && - ((window_rect.bottom - window_rect.top) == (info.rcMonitor.bottom - info.rcMonitor.top)) + (windowRect.left == info.rcMonitor.left) && + (windowRect.top == info.rcMonitor.top) && + ((windowRect.right - windowRect.left) == (info.rcMonitor.right - info.rcMonitor.left)) && + ((windowRect.bottom - windowRect.top) == (info.rcMonitor.bottom - info.rcMonitor.top)) ) return false; + if (!SetWindowPos( hwnd, HWND_TOP, @@ -387,67 +417,74 @@ static bool UpdateWindowSize( } UINT dpi = GetWindowDpi(hwnd); - float dpi_scale = ScaleFromDpi(dpi); - bool dpi_scaling = flags & FLAG_WINDOW_HIGHDPI; - SIZE desired = PxFromPt2(dpi_scale, dpi_scaling, app_screen_size); + float dpiScale = ScaleFromDpi(dpi); + bool dpiScaling = flags & FLAG_WINDOW_HIGHDPI; + SIZE desired = PxFromPt2(dpiScale, dpiScaling, appScreenSize); SIZE actual = GetClientSize(hwnd); if (actual.cx == desired.cx || actual.cy == desired.cy) return false; TRACELOG( LOG_INFO, - "restoring client size from %dx%d to %dx%d (dpi=%lu dpi_scaling=%d app=%fx%f)", + "restoring client size from %dx%d to %dx%d (dpi=%lu dpiScaling=%d app=%fx%f)", actual.cx, actual.cy, desired.cx, desired.cy, - dpi, dpi_scaling, - app_screen_size.x, app_screen_size.y + dpi, dpiScaling, + appScreenSize.x, appScreenSize.y ); - SIZE window_size = CalcWindowSize(dpi, desired, MakeWindowStyle(flags)); - POINT window_pos = (POINT){ 0, 0 }; - UINT swp_flags = SWP_NOZORDER | SWP_FRAMECHANGED; - if (kind == UPDATE_WINDOW_FIRST) { + SIZE windowSize = CalcWindowSize(dpi, desired, MakeWindowStyle(flags)); + POINT windowPos = (POINT){ 0, 0 }; + UINT swpFlags = SWP_NOZORDER | SWP_FRAMECHANGED; + if (kind == UPDATE_WINDOW_FIRST) + { HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); - if (!monitor) { + if (!monitor) + { LogFail("MonitorFromWindow", GetLastError()); abort(); } + MONITORINFO info; info.cbSize = sizeof(info); - if (!GetMonitorInfoW(monitor, &info)) { + if (!GetMonitorInfoW(monitor, &info)) + { LogFail("GetMonitorInfo", GetLastError()); abort(); } - LONG monitor_width = info.rcMonitor.right - info.rcMonitor.left; - LONG monitor_height = info.rcMonitor.bottom - info.rcMonitor.top; - window_pos = (POINT){ - MAX(0, (monitor_width - window_size.cx)/2), - MAX(0, (monitor_height - window_size.cy)/2), + + LONG monitorWidth = info.rcMonitor.right - info.rcMonitor.left; + LONG monitorHeight = info.rcMonitor.bottom - info.rcMonitor.top; + windowPos = (POINT){ + MAX(0, (monitorWidth - windowSize.cx)/2), + MAX(0, (monitorHeight - windowSize.cy)/2), }; } else { - swp_flags |= SWP_NOMOVE; + swpFlags |= SWP_NOMOVE; } + if (!SetWindowPos( hwnd, NULL, - window_pos.x, window_pos.y, - window_size.cx, window_size.cy, - swp_flags + windowPos.x, windowPos.y, + windowSize.cx, windowSize.cy, + swpFlags )) { LogFail("SetWindowPos", GetLastError()); abort(); } + return true; } #define CLASS_NAME L"RaylibWindow" -static void CreateWindowAlloca(const char* title, DWORD style) +static void CreateWindowAlloca(const char *title, DWORD style) { - WCHAR* title_wide; - AToWAlloca(title_wide, title); + WCHAR *titleWide; + A_TO_W_ALLOCA(titleWide, title); CreateWindowExW( WINDOW_STYLE_EX, CLASS_NAME, - title_wide, + titleWide, style, 0, 0, 0, 0, @@ -466,10 +503,12 @@ static BOOL IsWindows10Version1703OrGreaterWin32(void) ) = (DWORD (*)( RTL_OSVERSIONINFOEXW*, ULONG, ULONGLONG ))GetProcAddress(ntdll, "RtlVerifyVersionInfo"); - if (!Verify) { + if (!Verify) + { LogFail("GetProcAddress 'RtlVerifyVersionInfo'", GetLastError()); return 0; } + RTL_OSVERSIONINFOEXW osvi = { 0 }; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); osvi.dwMajorVersion = 10; @@ -482,239 +521,247 @@ static BOOL IsWindows10Version1703OrGreaterWin32(void) return 0 == (*Verify)(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, cond); } -static void* FindProc(const char* name) +static void *FindProc(const char *name) { { - void* proc = wglGetProcAddress(name); + void *proc = wglGetProcAddress(name); if (proc) return proc; } static HMODULE opengl = NULL; - if (!opengl) { + if (!opengl) + { opengl = LoadLibraryW(L"opengl32"); } - if (opengl) { - void* proc = GetProcAddress(opengl, name); + if (opengl) + { + void *proc = GetProcAddress(opengl, name); if (proc) return proc; } return NULL; } -static void* WglGetProcAddress(const char* name) +static void *WglGetProcAddress(const char *name) { - void* result = FindProc(name); - if (result) { + void *result = FindProc(name); + if (result) + { //TRACELOG(LOG_DEBUG, "GetProcAddress '%s' > %p", name, result); - } else { + } + else + { TRACELOG(LOG_ERROR, "GetProcAddress '%s' > %p failed, error=%u", name, result, GetLastError()); } + return result; } static KeyboardKey KeyFromWparam(WPARAM wparam) { - switch (wparam) { - /* case VK_LBUTTON: return KEY_; */ - /* case VK_RBUTTON: return KEY_; */ - /* case VK_CANCEL: return KEY_; */ - /* case VK_MBUTTON: return KEY_; */ - /* case VK_XBUTTON1: return KEY_; */ - /* case VK_XBUTTON2: return KEY_; */ - /* case VK_BACK: return KEY_; */ - /* case VK_TAB: return KEY_; */ - /* case VK_CLEAR: return KEY_; */ - case VK_RETURN: return KEY_ENTER; - /* case VK_SHIFT: return KEY_; */ - /* case VK_CONTROL: return KEY_; */ - /* case VK_MENU: return KEY_; */ - /* case VK_PAUSE: return KEY_; */ - /* case VK_CAPITAL: return KEY_; */ - /* case VK_KANA: return KEY_; */ - /* case VK_HANGUL: return KEY_; */ - /* case VK_IME_ON: return KEY_; */ - /* case VK_JUNJA: return KEY_; */ - /* case VK_FINAL: return KEY_; */ - /* case VK_HANJA: return KEY_; */ - /* case VK_KANJI: return KEY_; */ - /* case VK_IME_OFF: return KEY_; */ - case VK_ESCAPE: return KEY_ESCAPE; - /* case VK_CONVERT: return KEY_; */ - /* case VK_NONCONVERT: return KEY_; */ - /* case VK_ACCEPT: return KEY_; */ - /* case VK_MODECHANGE: return KEY_; */ - case VK_SPACE: return KEY_SPACE; - /* case VK_PRIOR: return KEY_; */ - /* case VK_NEXT: return KEY_; */ - /* case VK_END: return KEY_; */ - /* case VK_HOME: return KEY_; */ - case VK_LEFT: return KEY_LEFT; - case VK_UP: return KEY_UP; - case VK_RIGHT: return KEY_RIGHT; - case VK_DOWN: return KEY_DOWN; - /* case VK_SELECT: return KEY_; */ - /* case VK_PRINT: return KEY_; */ - /* case VK_EXECUTE: return KEY_; */ - /* case VK_SNAPSHOT: return KEY_; */ - /* case VK_INSERT: return KEY_; */ - /* case VK_DELETE: return KEY_; */ - /* case VK_HELP: return KEY_; */ - case '0': return KEY_ZERO; - case '1': return KEY_ONE; - case '2': return KEY_TWO; - case '3': return KEY_THREE; - case '4': return KEY_FOUR; - case '5': return KEY_FIVE; - case '6': return KEY_SIX; - case '7': return KEY_SEVEN; - case '8': return KEY_EIGHT; - case '9': return KEY_NINE; - /* case 0x3A-40: return KEY_; */ - case 'A': return KEY_A; - case 'B': return KEY_B; - case 'C': return KEY_C; - case 'D': return KEY_D; - case 'E': return KEY_E; - case 'F': return KEY_F; - case 'G': return KEY_G; - case 'H': return KEY_H; - case 'I': return KEY_I; - case 'J': return KEY_J; - case 'K': return KEY_K; - case 'L': return KEY_L; - case 'M': return KEY_M; - case 'N': return KEY_N; - case 'O': return KEY_O; - case 'P': return KEY_P; - case 'Q': return KEY_Q; - case 'R': return KEY_R; - case 'S': return KEY_S; - case 'T': return KEY_T; - case 'U': return KEY_U; - case 'V': return KEY_V; - case 'W': return KEY_W; - case 'X': return KEY_X; - case 'Y': return KEY_Y; - case 'Z': return KEY_Z; - /* case VK_LWIN: return KEY_; */ - /* case VK_RWIN: return KEY_; */ - /* case VK_APPS: return KEY_; */ - /* case VK_SLEEP: return KEY_; */ - /* case VK_NUMPAD0: return KEY_; */ - /* case VK_NUMPAD1: return KEY_; */ - /* case VK_NUMPAD2: return KEY_; */ - /* case VK_NUMPAD3: return KEY_; */ - /* case VK_NUMPAD4: return KEY_; */ - /* case VK_NUMPAD5: return KEY_; */ - /* case VK_NUMPAD6: return KEY_; */ - /* case VK_NUMPAD7: return KEY_; */ - /* case VK_NUMPAD8: return KEY_; */ - /* case VK_NUMPAD9: return KEY_; */ - /* case VK_MULTIPLY: return KEY_; */ - /* case VK_ADD: return KEY_; */ - /* case VK_SEPARATOR: return KEY_; */ - /* case VK_SUBTRACT: return KEY_; */ - /* case VK_DECIMAL: return KEY_; */ - /* case VK_DIVIDE: return KEY_; */ - /* case VK_F1: return KEY_; */ - /* case VK_F2: return KEY_; */ - /* case VK_F3: return KEY_; */ - /* case VK_F4: return KEY_; */ - /* case VK_F5: return KEY_; */ - /* case VK_F6: return KEY_; */ - /* case VK_F7: return KEY_; */ - /* case VK_F8: return KEY_; */ - /* case VK_F9: return KEY_; */ - /* case VK_F10: return KEY_; */ - /* case VK_F11: return KEY_; */ - /* case VK_F12: return KEY_; */ - /* case VK_F13: return KEY_; */ - /* case VK_F14: return KEY_; */ - /* case VK_F15: return KEY_; */ - /* case VK_F16: return KEY_; */ - /* case VK_F17: return KEY_; */ - /* case VK_F18: return KEY_; */ - /* case VK_F19: return KEY_; */ - /* case VK_F20: return KEY_; */ - /* case VK_F21: return KEY_; */ - /* case VK_F22: return KEY_; */ - /* case VK_F23: return KEY_; */ - /* case VK_F24: return KEY_; */ - /* case VK_NUMLOCK: return KEY_; */ - /* case VK_SCROLL: return KEY_; */ - /* case VK_LSHIFT: return KEY_; */ - /* case VK_RSHIFT: return KEY_; */ - /* case VK_LCONTROL: return KEY_; */ - /* case VK_RCONTROL: return KEY_; */ - /* case VK_LMENU: return KEY_; */ - /* case VK_RMENU: return KEY_; */ - /* case VK_BROWSER_BACK: return KEY_; */ - /* case VK_BROWSER_FORWARD: return KEY_; */ - /* case VK_BROWSER_REFRESH: return KEY_; */ - /* case VK_BROWSER_STOP: return KEY_; */ - /* case VK_BROWSER_SEARCH: return KEY_; */ - /* case VK_BROWSER_FAVORITES: return KEY_; */ - /* case VK_BROWSER_HOME: return KEY_; */ - /* case VK_VOLUME_MUTE: return KEY_; */ - /* case VK_VOLUME_DOWN: return KEY_; */ - /* case VK_VOLUME_UP: return KEY_; */ - /* case VK_MEDIA_NEXT_TRACK: return KEY_; */ - /* case VK_MEDIA_PREV_TRACK: return KEY_; */ - /* case VK_MEDIA_STOP: return KEY_; */ - /* case VK_MEDIA_PLAY_PAUSE: return KEY_; */ - /* case VK_LAUNCH_MAIL: return KEY_; */ - /* case VK_LAUNCH_MEDIA_SELECT: return KEY_; */ - /* case VK_LAUNCH_APP1: return KEY_; */ - /* case VK_LAUNCH_APP2: return KEY_; */ - /* case VK_OEM_1: return KEY_; */ - /* case VK_OEM_PLUS: return KEY_; */ - /* case VK_OEM_COMMA: return KEY_; */ - /* case VK_OEM_MINUS: return KEY_; */ - /* case VK_OEM_PERIOD: return KEY_; */ - /* case VK_OEM_2: return KEY_; */ - /* case VK_OEM_3: return KEY_; */ - /* case VK_OEM_4: return KEY_; */ - /* case VK_OEM_5: return KEY_; */ - /* case VK_OEM_6: return KEY_; */ - /* case VK_OEM_7: return KEY_; */ - /* case VK_OEM_8: return KEY_; */ - /* case VK_OEM_102: return KEY_; */ - /* case VK_PROCESSKEY: return KEY_; */ - /* case VK_PACKET: return KEY_; */ - /* case VK_ATTN: return KEY_; */ - /* case VK_CRSEL: return KEY_; */ - /* case VK_EXSEL: return KEY_; */ - /* case VK_EREOF: return KEY_; */ - /* case VK_PLAY: return KEY_; */ - /* case VK_ZOOM: return KEY_; */ - /* case VK_NONAME: return KEY_; */ - /* case VK_PA1: return KEY_; */ - /* case VK_OEM_CLEAR: return KEY_; */ - default: return KEY_NULL; + switch (wparam) + { + /* case VK_LBUTTON: return KEY_; */ + /* case VK_RBUTTON: return KEY_; */ + /* case VK_CANCEL: return KEY_; */ + /* case VK_MBUTTON: return KEY_; */ + /* case VK_XBUTTON1: return KEY_; */ + /* case VK_XBUTTON2: return KEY_; */ + /* case VK_BACK: return KEY_; */ + /* case VK_TAB: return KEY_; */ + /* case VK_CLEAR: return KEY_; */ + case VK_RETURN: return KEY_ENTER; + /* case VK_SHIFT: return KEY_; */ + /* case VK_CONTROL: return KEY_; */ + /* case VK_MENU: return KEY_; */ + /* case VK_PAUSE: return KEY_; */ + /* case VK_CAPITAL: return KEY_; */ + /* case VK_KANA: return KEY_; */ + /* case VK_HANGUL: return KEY_; */ + /* case VK_IME_ON: return KEY_; */ + /* case VK_JUNJA: return KEY_; */ + /* case VK_FINAL: return KEY_; */ + /* case VK_HANJA: return KEY_; */ + /* case VK_KANJI: return KEY_; */ + /* case VK_IME_OFF: return KEY_; */ + case VK_ESCAPE: return KEY_ESCAPE; + /* case VK_CONVERT: return KEY_; */ + /* case VK_NONCONVERT: return KEY_; */ + /* case VK_ACCEPT: return KEY_; */ + /* case VK_MODECHANGE: return KEY_; */ + case VK_SPACE: return KEY_SPACE; + /* case VK_PRIOR: return KEY_; */ + /* case VK_NEXT: return KEY_; */ + /* case VK_END: return KEY_; */ + /* case VK_HOME: return KEY_; */ + case VK_LEFT: return KEY_LEFT; + case VK_UP: return KEY_UP; + case VK_RIGHT: return KEY_RIGHT; + case VK_DOWN: return KEY_DOWN; + /* case VK_SELECT: return KEY_; */ + /* case VK_PRINT: return KEY_; */ + /* case VK_EXECUTE: return KEY_; */ + /* case VK_SNAPSHOT: return KEY_; */ + /* case VK_INSERT: return KEY_; */ + /* case VK_DELETE: return KEY_; */ + /* case VK_HELP: return KEY_; */ + case '0': return KEY_ZERO; + case '1': return KEY_ONE; + case '2': return KEY_TWO; + case '3': return KEY_THREE; + case '4': return KEY_FOUR; + case '5': return KEY_FIVE; + case '6': return KEY_SIX; + case '7': return KEY_SEVEN; + case '8': return KEY_EIGHT; + case '9': return KEY_NINE; + /* case 0x3A-40: return KEY_; */ + case 'A': return KEY_A; + case 'B': return KEY_B; + case 'C': return KEY_C; + case 'D': return KEY_D; + case 'E': return KEY_E; + case 'F': return KEY_F; + case 'G': return KEY_G; + case 'H': return KEY_H; + case 'I': return KEY_I; + case 'J': return KEY_J; + case 'K': return KEY_K; + case 'L': return KEY_L; + case 'M': return KEY_M; + case 'N': return KEY_N; + case 'O': return KEY_O; + case 'P': return KEY_P; + case 'Q': return KEY_Q; + case 'R': return KEY_R; + case 'S': return KEY_S; + case 'T': return KEY_T; + case 'U': return KEY_U; + case 'V': return KEY_V; + case 'W': return KEY_W; + case 'X': return KEY_X; + case 'Y': return KEY_Y; + case 'Z': return KEY_Z; + /* case VK_LWIN: return KEY_; */ + /* case VK_RWIN: return KEY_; */ + /* case VK_APPS: return KEY_; */ + /* case VK_SLEEP: return KEY_; */ + /* case VK_NUMPAD0: return KEY_; */ + /* case VK_NUMPAD1: return KEY_; */ + /* case VK_NUMPAD2: return KEY_; */ + /* case VK_NUMPAD3: return KEY_; */ + /* case VK_NUMPAD4: return KEY_; */ + /* case VK_NUMPAD5: return KEY_; */ + /* case VK_NUMPAD6: return KEY_; */ + /* case VK_NUMPAD7: return KEY_; */ + /* case VK_NUMPAD8: return KEY_; */ + /* case VK_NUMPAD9: return KEY_; */ + /* case VK_MULTIPLY: return KEY_; */ + /* case VK_ADD: return KEY_; */ + /* case VK_SEPARATOR: return KEY_; */ + /* case VK_SUBTRACT: return KEY_; */ + /* case VK_DECIMAL: return KEY_; */ + /* case VK_DIVIDE: return KEY_; */ + /* case VK_F1: return KEY_; */ + /* case VK_F2: return KEY_; */ + /* case VK_F3: return KEY_; */ + /* case VK_F4: return KEY_; */ + /* case VK_F5: return KEY_; */ + /* case VK_F6: return KEY_; */ + /* case VK_F7: return KEY_; */ + /* case VK_F8: return KEY_; */ + /* case VK_F9: return KEY_; */ + /* case VK_F10: return KEY_; */ + /* case VK_F11: return KEY_; */ + /* case VK_F12: return KEY_; */ + /* case VK_F13: return KEY_; */ + /* case VK_F14: return KEY_; */ + /* case VK_F15: return KEY_; */ + /* case VK_F16: return KEY_; */ + /* case VK_F17: return KEY_; */ + /* case VK_F18: return KEY_; */ + /* case VK_F19: return KEY_; */ + /* case VK_F20: return KEY_; */ + /* case VK_F21: return KEY_; */ + /* case VK_F22: return KEY_; */ + /* case VK_F23: return KEY_; */ + /* case VK_F24: return KEY_; */ + /* case VK_NUMLOCK: return KEY_; */ + /* case VK_SCROLL: return KEY_; */ + /* case VK_LSHIFT: return KEY_; */ + /* case VK_RSHIFT: return KEY_; */ + /* case VK_LCONTROL: return KEY_; */ + /* case VK_RCONTROL: return KEY_; */ + /* case VK_LMENU: return KEY_; */ + /* case VK_RMENU: return KEY_; */ + /* case VK_BROWSER_BACK: return KEY_; */ + /* case VK_BROWSER_FORWARD: return KEY_; */ + /* case VK_BROWSER_REFRESH: return KEY_; */ + /* case VK_BROWSER_STOP: return KEY_; */ + /* case VK_BROWSER_SEARCH: return KEY_; */ + /* case VK_BROWSER_FAVORITES: return KEY_; */ + /* case VK_BROWSER_HOME: return KEY_; */ + /* case VK_VOLUME_MUTE: return KEY_; */ + /* case VK_VOLUME_DOWN: return KEY_; */ + /* case VK_VOLUME_UP: return KEY_; */ + /* case VK_MEDIA_NEXT_TRACK: return KEY_; */ + /* case VK_MEDIA_PREV_TRACK: return KEY_; */ + /* case VK_MEDIA_STOP: return KEY_; */ + /* case VK_MEDIA_PLAY_PAUSE: return KEY_; */ + /* case VK_LAUNCH_MAIL: return KEY_; */ + /* case VK_LAUNCH_MEDIA_SELECT: return KEY_; */ + /* case VK_LAUNCH_APP1: return KEY_; */ + /* case VK_LAUNCH_APP2: return KEY_; */ + /* case VK_OEM_1: return KEY_; */ + /* case VK_OEM_PLUS: return KEY_; */ + /* case VK_OEM_COMMA: return KEY_; */ + /* case VK_OEM_MINUS: return KEY_; */ + /* case VK_OEM_PERIOD: return KEY_; */ + /* case VK_OEM_2: return KEY_; */ + /* case VK_OEM_3: return KEY_; */ + /* case VK_OEM_4: return KEY_; */ + /* case VK_OEM_5: return KEY_; */ + /* case VK_OEM_6: return KEY_; */ + /* case VK_OEM_7: return KEY_; */ + /* case VK_OEM_8: return KEY_; */ + /* case VK_OEM_102: return KEY_; */ + /* case VK_PROCESSKEY: return KEY_; */ + /* case VK_PACKET: return KEY_; */ + /* case VK_ATTN: return KEY_; */ + /* case VK_CRSEL: return KEY_; */ + /* case VK_EXSEL: return KEY_; */ + /* case VK_EREOF: return KEY_; */ + /* case VK_PLAY: return KEY_; */ + /* case VK_ZOOM: return KEY_; */ + /* case VK_NONAME: return KEY_; */ + /* case VK_PA1: return KEY_; */ + /* case VK_OEM_CLEAR: return KEY_; */ + default: return KEY_NULL; } } static LPCWSTR GetCursorName(int cursor) { - switch (cursor) { - case MOUSE_CURSOR_DEFAULT : return IDC_ARROW; - case MOUSE_CURSOR_ARROW : return IDC_ARROW; - case MOUSE_CURSOR_IBEAM : return IDC_IBEAM; - case MOUSE_CURSOR_CROSSHAIR : return IDC_CROSS; - case MOUSE_CURSOR_POINTING_HAND: return IDC_HAND; - case MOUSE_CURSOR_RESIZE_EW : return IDC_SIZEWE; - case MOUSE_CURSOR_RESIZE_NS : return IDC_SIZENS; - case MOUSE_CURSOR_RESIZE_NWSE : return IDC_SIZENWSE; - case MOUSE_CURSOR_RESIZE_NESW : return IDC_SIZENESW; - case MOUSE_CURSOR_RESIZE_ALL : return IDC_SIZEALL; - case MOUSE_CURSOR_NOT_ALLOWED : return IDC_NO; - default: abort(); + switch (cursor) + { + case MOUSE_CURSOR_DEFAULT : return IDC_ARROW; + case MOUSE_CURSOR_ARROW : return IDC_ARROW; + case MOUSE_CURSOR_IBEAM : return IDC_IBEAM; + case MOUSE_CURSOR_CROSSHAIR : return IDC_CROSS; + case MOUSE_CURSOR_POINTING_HAND: return IDC_HAND; + case MOUSE_CURSOR_RESIZE_EW : return IDC_SIZEWE; + case MOUSE_CURSOR_RESIZE_NS : return IDC_SIZENS; + case MOUSE_CURSOR_RESIZE_NWSE : return IDC_SIZENWSE; + case MOUSE_CURSOR_RESIZE_NESW : return IDC_SIZENESW; + case MOUSE_CURSOR_RESIZE_ALL : return IDC_SIZEALL; + case MOUSE_CURSOR_NOT_ALLOWED : return IDC_NO; + default: abort(); } } static BOOL CALLBACK CountMonitorsProc(HMONITOR handle, HDC _, LPRECT rect, LPARAM lparam) { - int* count = (int*)lparam; + int *count = (int*)lparam; *count += 1; // always return TRUE to continue the loop, otherwise, the caller // can't distinguish between stopping the loop and an error @@ -724,16 +771,19 @@ static BOOL CALLBACK CountMonitorsProc(HMONITOR handle, HDC _, LPRECT rect, LPAR typedef struct { HMONITOR needle; int index; - int match_index; + int matchIndex; RECT rect; } FindMonitorContext; + static BOOL CALLBACK FindMonitorProc(HMONITOR handle, HDC _, LPRECT rect, LPARAM lparam) { - FindMonitorContext* c = (FindMonitorContext*)lparam; - if (handle == c->needle) { - c->match_index = c->index; + FindMonitorContext *c = (FindMonitorContext*)lparam; + if (handle == c->needle) + { + c->matchIndex = c->index; c->rect = *rect; } + c->index += 1; // always return TRUE to continue the loop, otherwise, the caller // can't distinguish between stopping the loop and an error @@ -746,58 +796,78 @@ typedef struct { } FlagsOp; static void GetStyleChangeFlagOps( - DWORD core_window_flags, - STYLESTRUCT* ss, - FlagsOp* deferred_flags + DWORD coreWindowFlags, + STYLESTRUCT *ss, + FlagsOp *deferredFlags ) { { - bool resizable = (core_window_flags & FLAG_WINDOW_RESIZABLE); - bool resizable_old = ResizableFromStyle(ss->styleOld); - bool resizable_new = ResizableFromStyle(ss->styleNew); - if (resizable != resizable_old) { - TRACELOG(LOG_ERROR, "expected resizable %u but got %u", resizable, resizable_old); + bool resizable = (coreWindowFlags & FLAG_WINDOW_RESIZABLE); + bool resizableOld = ResizableFromStyle(ss->styleOld); + bool resizableNew = ResizableFromStyle(ss->styleNew); + if (resizable != resizableOld) + { + TRACELOG(LOG_ERROR, "expected resizable %u but got %u", resizable, resizableOld); abort(); } - if (resizable_old != resizable_new) { - //TRACELOG(LOG_INFO, "resizable = %u", resizable_new); - if (resizable_new) { - deferred_flags->set |= FLAG_WINDOW_RESIZABLE; - } else { - deferred_flags->clear |= FLAG_WINDOW_RESIZABLE; + + if (resizableOld != resizableNew) + { + //TRACELOG(LOG_INFO, "resizable = %u", resizableNew); + if (resizableNew) + { + deferredFlags->set |= FLAG_WINDOW_RESIZABLE; + } + else + { + deferredFlags->clear |= FLAG_WINDOW_RESIZABLE; } } } + { - bool decorated = (0 == (core_window_flags & FLAG_WINDOW_UNDECORATED)); - bool decorated_old = DecoratedFromStyle(ss->styleOld); - bool decorated_new = DecoratedFromStyle(ss->styleNew); - if (decorated != decorated_old) { - TRACELOG(LOG_ERROR, "expected decorated %u but got %u", decorated, decorated_old); + bool decorated = (0 == (coreWindowFlags & FLAG_WINDOW_UNDECORATED)); + bool decoratedOld = DecoratedFromStyle(ss->styleOld); + bool decoratedNew = DecoratedFromStyle(ss->styleNew); + if (decorated != decoratedOld) + { + TRACELOG(LOG_ERROR, "expected decorated %u but got %u", decorated, decoratedOld); abort(); } - if (decorated_old != decorated_new) { - //TRACELOG(LOG_INFO, "decorated = %u", decorated_new); - if (decorated_new) { - deferred_flags->clear |= FLAG_WINDOW_UNDECORATED; - } else { - deferred_flags->set |= FLAG_WINDOW_UNDECORATED; + + if (decoratedOld != decoratedNew) + { + //TRACELOG(LOG_INFO, "decorated = %u", decoratedNew); + if (decoratedNew) + { + deferredFlags->clear |= FLAG_WINDOW_UNDECORATED; + } + else + { + deferredFlags->set |= FLAG_WINDOW_UNDECORATED; } } } + { - bool hidden = (core_window_flags & FLAG_WINDOW_HIDDEN); - bool hidden_old = HiddenFromStyle(ss->styleOld); - bool hidden_new = HiddenFromStyle(ss->styleNew); - if (hidden != hidden_old) { - TRACELOG(LOG_ERROR, "expected hidden %u but got %u", hidden, hidden_old); + bool hidden = (coreWindowFlags & FLAG_WINDOW_HIDDEN); + bool hiddenOld = HiddenFromStyle(ss->styleOld); + bool hiddenNew = HiddenFromStyle(ss->styleNew); + if (hidden != hiddenOld) + { + TRACELOG(LOG_ERROR, "expected hidden %u but got %u", hidden, hiddenOld); abort(); } - if (hidden_old != hidden_new) { - TRACELOG(LOG_INFO, "hidden = %u", hidden_new); - if (hidden_new) { - deferred_flags->set |= FLAG_WINDOW_HIDDEN; - } else { - deferred_flags->clear |= FLAG_WINDOW_HIDDEN; + + if (hiddenOld != hiddenNew) + { + TRACELOG(LOG_INFO, "hidden = %u", hiddenNew); + if (hiddenNew) + { + deferredFlags->set |= FLAG_WINDOW_HIDDEN; + } + else + { + deferredFlags->clear |= FLAG_WINDOW_HIDDEN; } } } @@ -825,13 +895,13 @@ static bool AdoptWindowResize(unsigned flags) // The last screen size requested by the app, the backend must keep the client area // this size (after DPI scaling is applied) when the window isn't fullscreen/maximized/minimized. -static Vector2 global_app_screen_size = (Vector2){0, 0}; -static unsigned global_desired_flags = 0; -static HWND global_hwnd = NULL; -static HDC global_hdc = NULL; -static HGLRC global_gl_context = NULL; -static LARGE_INTEGER global_timer_frequency; -static bool global_cursor_enabled = true; +static Vector2 globalAppScreenSize = (Vector2){0, 0}; +static unsigned globalDesiredFlags = 0; +static HWND globalHwnd = NULL; +static HDC globalHdc = NULL; +static HGLRC globalGlContext = NULL; +static LARGE_INTEGER globalTimerFrequency; +static bool globalCursorEnabled = true; static void HandleKey(WPARAM wparam, LPARAM lparam, char state) { @@ -839,13 +909,17 @@ static void HandleKey(WPARAM wparam, LPARAM lparam, char state) // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! /* BYTE scancode = lparam >> 16; */ /* TRACELOG(LOG_INFO, "KEY key=%d vk=%lu scan=%u = %u", key, wparam, scancode, state); */ - if (key != KEY_NULL) { + if (key != KEY_NULL) + { /* TRACELOG(LOG_INFO, "KEY[%d] = %d (old=%d)\n", key, state, CORE.Input.Keyboard.currentKeyState[key]); */ CORE.Input.Keyboard.currentKeyState[key] = state; - if (key == KEY_ESCAPE && state == 1) { + if (key == KEY_ESCAPE && state == 1) + { CORE.Window.shouldClose = 1; } - } else { + } + else + { TRACELOG(LOG_WARNING, "unknown (or currently unhandled) virtual keycode %d (0x%x)", wparam, wparam); } @@ -860,26 +934,31 @@ static void HandleMouseButton(int button, char state) static void HandleRawInput(LPARAM lparam) { RAWINPUT input; + { - UINT input_size = sizeof(input); - UINT size = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &input, &input_size, sizeof(RAWINPUTHEADER)); + UINT inputSize = sizeof(input); + UINT size = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &input, &inputSize, sizeof(RAWINPUTHEADER)); if (size == (UINT)-1) { LogFail("GetRawInputData", GetLastError()); abort(); } - if (size != input_size) abort(); // bug? + if (size != inputSize) abort(); // bug? } - if (input.header.dwType != RIM_TYPEMOUSE) { + if (input.header.dwType != RIM_TYPEMOUSE) + { TRACELOG(LOG_ERROR, "Unexpected WM_INPUT type %lu", input.header.dwType); abort(); } - if (input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { + if (input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) + { TRACELOG(LOG_ERROR, "TODO: handle absolute mouse inputs!"); abort(); } - if (input.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) { + + if (input.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) + { TRACELOG(LOG_ERROR, "TODO: handle virtual desktop mouse inputs!"); abort(); } @@ -892,34 +971,36 @@ static void HandleRawInput(LPARAM lparam) if (CORE.Input.Mouse.currentPosition.y != 0) abort(); } -static void HandleWindowResize(HWND hwnd, Vector2* app_screen_size_ref) +static void HandleWindowResize(HWND hwnd, Vector2 *appScreenSizeRef) { if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return; - SIZE client_size = GetClientSize(hwnd); - TRACELOG(LOG_DEBUG, "NewClientSize %lux%lu", client_size.cx, client_size.cy); - /* CORE.Window.currentFbo.width = client_size.cx; */ - /* CORE.Window.currentFbo.height = client_size.cy; */ - /* glViewport(0, 0, client_size.cx, client_size.cy); */ - SetupViewport(client_size.cx, client_size.cy); + SIZE clientSize = GetClientSize(hwnd); + TRACELOG(LOG_DEBUG, "NewClientSize %lux%lu", clientSize.cx, clientSize.cy); + /* CORE.Window.currentFbo.width = clientSize.cx; */ + /* CORE.Window.currentFbo.height = clientSize.cy; */ + /* glViewport(0, 0, clientSize.cx, clientSize.cy); */ + SetupViewport(clientSize.cx, clientSize.cy); CORE.Window.resizedLastFrame = true; - float dpi_scale = ScaleFromDpi(GetWindowDpi(hwnd)); + float dpiScale = ScaleFromDpi(GetWindowDpi(hwnd)); bool highdpi = !!(CORE.Window.flags & FLAG_WINDOW_HIGHDPI); - Vector2 screen_size = (Vector2){ - PtFromPx(dpi_scale, highdpi, client_size.cx), - PtFromPx(dpi_scale, highdpi, client_size.cy), + Vector2 screenSize = (Vector2){ + PtFromPx(dpiScale, highdpi, clientSize.cx), + PtFromPx(dpiScale, highdpi, clientSize.cy), }; - CORE.Window.screen.width = roundf(screen_size.x); - CORE.Window.screen.height = roundf(screen_size.y); - if (AdoptWindowResize(CORE.Window.flags)) { + CORE.Window.screen.width = roundf(screenSize.x); + CORE.Window.screen.height = roundf(screenSize.y); + if (AdoptWindowResize(CORE.Window.flags)) + { TRACELOG( LOG_DEBUG, "updating app size to %fx%f from window resize", - screen_size.x, screen_size.y + screenSize.x, screenSize.y ); - *app_screen_size_ref = screen_size; + *appScreenSizeRef = screenSize; } + CORE.Window.screenScale = MatrixScale( (float)CORE.Window.render.width/CORE.Window.screen.width, (float)CORE.Window.render.height/CORE.Window.screen.height, @@ -927,15 +1008,17 @@ static void HandleWindowResize(HWND hwnd, Vector2* app_screen_size_ref) ); } -static void UpdateWindowStyle(HWND hwnd, unsigned desired_flags) +static void UpdateWindowStyle(HWND hwnd, unsigned desiredFlags) { { DWORD current = STYLE_MASK_WRITABLE & MakeWindowStyle(CORE.Window.flags); - DWORD desired = STYLE_MASK_WRITABLE & MakeWindowStyle(desired_flags); - if (current != desired) { + DWORD desired = STYLE_MASK_WRITABLE & MakeWindowStyle(desiredFlags); + if (current != desired) + { SetLastError(0); DWORD previous = STYLE_MASK_WRITABLE & SetWindowLongPtrW(hwnd, GWL_STYLE, desired); - if (previous != current) { + if (previous != current) + { TRACELOG( LOG_ERROR, "SetWindowLong returned writable flags 0x%x but expected 0x%x (diff=0x%x, error=%lu)", @@ -946,16 +1029,21 @@ static void UpdateWindowStyle(HWND hwnd, unsigned desired_flags) ); abort(); } - CheckFlags("UpdateWindowStyle", hwnd, desired_flags, desired, STYLE_MASK_WRITABLE); + + CheckFlags("UpdateWindowStyle", hwnd, desiredFlags, desired, STYLE_MASK_WRITABLE); } } - Mized current_mized = MizedFromStyle(MakeWindowStyle(CORE.Window.flags)); - Mized desired_mized = MizedFromStyle(MakeWindowStyle(desired_flags)); - if (current_mized != desired_mized) switch (desired_mized) { - case MIZED_NONE: ShowWindow(hwnd, SW_RESTORE); break; - case MIZED_MIN: ShowWindow(hwnd, SW_MINIMIZE); break; - case MIZED_MAX: ShowWindow(hwnd, SW_MAXIMIZE); break; + Mized currentMized = MizedFromStyle(MakeWindowStyle(CORE.Window.flags)); + Mized desiredMized = MizedFromStyle(MakeWindowStyle(desiredFlags)); + if (currentMized != desiredMized) + { + switch (desiredMized) + { + case MIZED_NONE: ShowWindow(hwnd, SW_RESTORE); break; + case MIZED_MIN: ShowWindow(hwnd, SW_MINIMIZE); break; + case MIZED_MAX: ShowWindow(hwnd, SW_MAXIMIZE); break; + } } } @@ -963,21 +1051,25 @@ typedef enum { SANITIZE_FLAGS_FIRST, SANITIZE_FLAGS_NORMAL, } SanitizeFlagsKind; + static unsigned SanitizeFlags(SanitizeFlagsKind kind, unsigned flags) { - if ((flags & FLAG_WINDOW_MAXIMIZED) && (flags & FLAG_BORDERLESS_WINDOWED_MODE)) { + if ((flags & FLAG_WINDOW_MAXIMIZED) && (flags & FLAG_BORDERLESS_WINDOWED_MODE)) + { TRACELOG(LOG_INFO, "borderless windows mode is overriding maximized"); flags &= ~FLAG_WINDOW_MAXIMIZED; } - switch (kind) { - case SANITIZE_FLAGS_FIRST: break; - case SANITIZE_FLAGS_NORMAL: break; - if ((flags & FLAG_MSAA_4X_HINT) && (!(CORE.Window.flags & FLAG_MSAA_4X_HINT))) { - TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); - flags &= ~FLAG_MSAA_4X_HINT; - } - break; + switch (kind) + { + case SANITIZE_FLAGS_FIRST: break; + case SANITIZE_FLAGS_NORMAL: break; + if ((flags & FLAG_MSAA_4X_HINT) && (!(CORE.Window.flags & FLAG_MSAA_4X_HINT))) + { + TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); + flags &= ~FLAG_MSAA_4X_HINT; + } + break; } return flags; @@ -1008,53 +1100,60 @@ static unsigned SanitizeFlags(SanitizeFlagsKind kind, unsigned flags) // state. This implementation is able to handle any/all of these special situations with a // retry loop that continues until we either reach the desired state or the state stops // changing. -static void UpdateFlags(HWND hwnd, unsigned desired_flags, Vector2 app_screen_size) +static void UpdateFlags(HWND hwnd, unsigned desiredFlags, Vector2 appScreenSize) { // flags that just apply immediately without needing any operations - CORE.Window.flags |= (desired_flags & FLAG_MASK_NO_UPDATE); + CORE.Window.flags |= (desiredFlags & FLAG_MASK_NO_UPDATE); { - int vsync = (CORE.Window.flags & FLAG_VSYNC_HINT) ? 1 : 0; + int vsync = (CORE.Window.flags & FLAG_VSYNC_HINT)? 1 : 0; PFNWGLSWAPINTERVALEXTPROC f = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); - if (f) { + if (f) + { (*f)(vsync); - if (vsync) { + if (vsync) + { CORE.Window.flags |= FLAG_VSYNC_HINT; - } else { + } + else + { CORE.Window.flags &= ~FLAG_VSYNC_HINT; } } } - DWORD previous_style; - for (unsigned attempt = 1; ; attempt++) { + DWORD previousStyle; + for (unsigned attempt = 1; ; attempt++) + { CheckFlags("UpdateFlags", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), STYLE_MASK_ALL); - bool window_size_updated = false; - if (MakeWindowStyle(CORE.Window.flags) == MakeWindowStyle(desired_flags)) { - window_size_updated = UpdateWindowSize(UPDATE_WINDOW_NORMAL, hwnd, app_screen_size, desired_flags); - if ((FLAG_MASK_REQUIRED & desired_flags) == (FLAG_MASK_REQUIRED & CORE.Window.flags)) + bool windowSizeUpdated = false; + if (MakeWindowStyle(CORE.Window.flags) == MakeWindowStyle(desiredFlags)) + { + windowSizeUpdated = UpdateWindowSize(UPDATE_WINDOW_NORMAL, hwnd, appScreenSize, desiredFlags); + if ((FLAG_MASK_REQUIRED & desiredFlags) == (FLAG_MASK_REQUIRED & CORE.Window.flags)) break; } if ( (attempt > 1) && - (previous_style == MakeWindowStyle(CORE.Window.flags)) && - !window_size_updated + (previousStyle == MakeWindowStyle(CORE.Window.flags)) && + !windowSizeUpdated ) { TRACELOG( LOG_ERROR, // TODO: print more information "UpdateFlags failed after %u attempt(s) wanted 0x%x but is 0x%x (diff=0x%x)", attempt, - desired_flags, + desiredFlags, CORE.Window.flags, - desired_flags ^ CORE.Window.flags + desiredFlags ^ CORE.Window.flags ); abort(); } - previous_style = MakeWindowStyle(CORE.Window.flags); - UpdateWindowStyle(hwnd, desired_flags); + + previousStyle = MakeWindowStyle(CORE.Window.flags); + UpdateWindowStyle(hwnd, desiredFlags); } } @@ -1071,9 +1170,12 @@ void ToggleFullscreen(void) void ToggleBorderlessWindowed(void) { - if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) { + if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) + { ClearWindowState(FLAG_BORDERLESS_WINDOWED_MODE); - } else { + } + else + { SetWindowState(FLAG_BORDERLESS_WINDOWED_MODE); } } @@ -1092,9 +1194,12 @@ void MinimizeWindow(void) void RestoreWindow(void) { if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) && - (CORE.Window.flags & FLAG_WINDOW_MINIMIZED)) { + (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) + ) { ClearWindowState(FLAG_WINDOW_MINIMIZED); - } else { + } + else + { ClearWindowState(FLAG_WINDOW_MINIMIZED|FLAG_WINDOW_MAXIMIZED); } } @@ -1102,15 +1207,15 @@ void RestoreWindow(void) // Set window configuration state using flags void SetWindowState(unsigned int flags) { - global_desired_flags = SanitizeFlags(SANITIZE_FLAGS_NORMAL, CORE.Window.flags | flags); - UpdateFlags(global_hwnd, global_desired_flags, global_app_screen_size); + globalDesiredFlags = SanitizeFlags(SANITIZE_FLAGS_NORMAL, CORE.Window.flags | flags); + UpdateFlags(globalHwnd, globalDesiredFlags, globalAppScreenSize); } // Clear window configuration state flags void ClearWindowState(unsigned int flags) { - global_desired_flags = SanitizeFlags(SANITIZE_FLAGS_NORMAL, CORE.Window.flags & ~flags); - UpdateFlags(global_hwnd, global_desired_flags, global_app_screen_size); + globalDesiredFlags = SanitizeFlags(SANITIZE_FLAGS_NORMAL, CORE.Window.flags & ~flags); + UpdateFlags(globalHwnd, globalDesiredFlags, globalAppScreenSize); } // Set icon for window @@ -1130,9 +1235,10 @@ void SetWindowIcons(Image *images, int count) void SetWindowTitle(const char *title) { CORE.Window.title = title; - WCHAR* titlew; - AToWAlloca(titlew, CORE.Window.title); - if (!SetWindowTextW(global_hwnd, titlew)) { + WCHAR *titlew; + A_TO_W_ALLOCA(titlew, CORE.Window.title); + if (!SetWindowTextW(globalHwnd, titlew)) + { LogFail("SetWindowText", GetLastError()); abort(); } @@ -1193,24 +1299,27 @@ void SetWindowFocused(void) // Get native window handle void *GetWindowHandle(void) { - return global_hwnd; + return globalHwnd; } int GetMonitorCount(void) { int count = 0; - if (!EnumDisplayMonitors(NULL, NULL, CountMonitorsProc, (LPARAM)&count)) { + if (!EnumDisplayMonitors(NULL, NULL, CountMonitorsProc, (LPARAM)&count)) + { LogFail("EnumDisplayMonitors", GetLastError()); abort(); } + return count; } // Get current monitor where window is placed int GetCurrentMonitor(void) { - HMONITOR monitor = MonitorFromWindow(global_hwnd, MONITOR_DEFAULTTOPRIMARY); - if (!monitor) { + HMONITOR monitor = MonitorFromWindow(globalHwnd, MONITOR_DEFAULTTOPRIMARY); + if (!monitor) + { LogFail("MonitorFromWindow", GetLastError()); abort(); } @@ -1218,12 +1327,14 @@ int GetCurrentMonitor(void) FindMonitorContext context; context.needle = monitor; context.index = 0; - context.match_index = -1; - if (!EnumDisplayMonitors(NULL, NULL, FindMonitorProc, (LPARAM)&context)) { + context.matchIndex = -1; + if (!EnumDisplayMonitors(NULL, NULL, FindMonitorProc, (LPARAM)&context)) + { LogFail("EnumDisplayMonitors", GetLastError()); abort(); } - return context.match_index; + + return context.matchIndex; } // Get selected monitor position @@ -1293,7 +1404,7 @@ Vector2 GetWindowPosition(void) // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { - float scale = ScaleFromDpi(GetWindowDpi(global_hwnd)); + float scale = ScaleFromDpi(GetWindowDpi(globalHwnd)); return (Vector2){ scale, scale }; } @@ -1337,10 +1448,14 @@ void HideCursor(void) // Enables cursor (unlock cursor) void EnableCursor(void) { - if (global_cursor_enabled) { + if (globalCursorEnabled) + { TRACELOG(LOG_INFO, "EnableCursor: already enabled"); - } else { - if (!ClipCursor(NULL)) { + } + else + { + if (!ClipCursor(NULL)) + { LogFail("ClipCursor", GetLastError()); abort(); } @@ -1358,7 +1473,7 @@ void EnableCursor(void) } ShowCursor(); - global_cursor_enabled = true; + globalCursorEnabled = true; TRACELOG(LOG_INFO, "EnableCursor: enabled"); } } @@ -1366,68 +1481,79 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { - if (global_cursor_enabled) { + if (globalCursorEnabled) + { { RAWINPUTDEVICE rid; rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE rid.dwFlags = RIDEV_INPUTSINK; // Add to this window even in background - rid.hwndTarget = global_hwnd; + rid.hwndTarget = globalHwnd; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { LogFail("RegisterRawInputDevices", GetLastError()); abort(); } } - RECT client_rect; - if (!GetClientRect(global_hwnd, &client_rect)) { + RECT clientRect; + if (!GetClientRect(globalHwnd, &clientRect)) + { LogFail("GetClientRect", GetLastError()); abort(); } - POINT topleft = { client_rect.left, client_rect.top }; - if (!ClientToScreen(global_hwnd, &topleft)) { + + POINT topleft = { clientRect.left, clientRect.top }; + if (!ClientToScreen(globalHwnd, &topleft)) + { LogFail("ClientToScreen", GetLastError()); abort(); } - LONG width = client_rect.right - client_rect.left; - LONG height = client_rect.bottom - client_rect.top; + + LONG width = clientRect.right - clientRect.left; + LONG height = clientRect.bottom - clientRect.top; TRACELOG(LOG_INFO, "ClipCursor client %d,%d %d,%d (topleft %d,%d)", - client_rect.left, - client_rect.top, - client_rect.right, - client_rect.bottom, + clientRect.left, + clientRect.top, + clientRect.right, + clientRect.bottom, topleft.x, topleft.y ); - LONG center_x = topleft.x + width / 2; - LONG center_y = topleft.y + height / 2; - RECT clip_rect = { center_x, center_y, center_x + 1, center_y + 1 }; - if (!ClipCursor(&clip_rect)) { + LONG centerX = topleft.x + width/2; + LONG centerY = topleft.y + height/2; + RECT clipRect = { centerX, centerY, centerX + 1, centerY + 1 }; + if (!ClipCursor(&clipRect)) + { LogFail("ClipCursor", GetLastError()); abort(); } + CORE.Input.Mouse.previousPosition = (Vector2){ 0, 0 }; CORE.Input.Mouse.currentPosition = (Vector2){ 0, 0 }; HideCursor(); - global_cursor_enabled = false; + globalCursorEnabled = false; TRACELOG(LOG_INFO, "DisableCursor: disabled"); - } else { + } + else + { TRACELOG(LOG_INFO, "DisableCursor: already disabled"); } } void SwapScreenBuffer(void) { - if (!global_hdc) abort(); - if (!SwapBuffers(global_hdc)) { + if (!globalHdc) abort(); + if (!SwapBuffers(globalHdc)) + { LogFail("SwapBuffers", GetLastError()); abort(); } - if (!ValidateRect(global_hwnd, NULL)) { + if (!ValidateRect(globalHwnd, NULL)) + { LogFail("ValidateRect", GetLastError()); abort(); } @@ -1437,7 +1563,7 @@ double GetTime(void) { LARGE_INTEGER now; QueryPerformanceCounter(&now); - return (double)(now.QuadPart - CORE.Time.base) / (double)global_timer_frequency.QuadPart; + return (double)(now.QuadPart - CORE.Time.base)/(double)globalTimerFrequency.QuadPart; } // Open URL with default system browser (if available) @@ -1475,12 +1601,15 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float d void SetMousePosition(int x, int y) { - if (global_cursor_enabled) { + if (globalCursorEnabled) + { CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; TRACELOG(LOG_WARNING, "SetMousePosition not implemented"); assert(0); // crash debug builds only - } else { + } + else + { TRACELOG(LOG_ERROR, "Possible? Should we allow this?"); abort(); } @@ -1488,12 +1617,14 @@ void SetMousePosition(int x, int y) void SetMouseCursor(int cursor) { - LPCWSTR cursor_name = GetCursorName(cursor); - HCURSOR hcursor = LoadCursorW(NULL, cursor_name); - if (!hcursor) { - TRACELOG(LOG_ERROR, "LoadCursor %d (win32 %d) failed, error=%lu", cursor, (size_t)cursor_name, GetLastError()); + LPCWSTR cursorName = GetCursorName(cursor); + HCURSOR hcursor = LoadCursorW(NULL, cursorName); + if (!hcursor) + { + TRACELOG(LOG_ERROR, "LoadCursor %d (win32 %d) failed, error=%lu", cursor, (size_t)cursorName, GetLastError()); abort(); } + SetCursor(hcursor); CORE.Input.Mouse.cursorHidden = false; } @@ -1542,7 +1673,8 @@ void PollInputEvents(void) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; MSG msg; - while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) + { if (msg.message == WM_PAINT) return; TranslateMessage(&msg); @@ -1554,310 +1686,354 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -#define WM_APP_UPDATE_WINDOW_SIZE (WM_APP+1) +#define WM_APP_UPDATE_WINDOW_SIZE (WM_APP + 1) -static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, FlagsOp* deferred_flags) +static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, FlagsOp *deferredFlags) { - switch (msg) { - case WM_CREATE: { - global_hdc = GetDC(hwnd); - if (!global_hdc) { - LogFail("GetDC", GetLastError()); - return -1; - } + switch (msg) + { + case WM_CREATE: + { + globalHdc = GetDC(hwnd); + if (!globalHdc) + { + LogFail("GetDC", GetLastError()); + return -1; + } - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) { - TRACELOG(LOG_ERROR, "TODO: implement FLAG_MSAA_4X_HINT"); - abort(); - } + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + TRACELOG(LOG_ERROR, "TODO: implement FLAG_MSAA_4X_HINT"); + abort(); + } - PIXELFORMATDESCRIPTOR pixelFormatDesc = {0}; - pixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR); - pixelFormatDesc.nVersion = 1; - pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; - pixelFormatDesc.iPixelType = PFD_TYPE_RGBA; - pixelFormatDesc.cColorBits = 32; - pixelFormatDesc.cAlphaBits = 8; - pixelFormatDesc.cDepthBits = 24; + PIXELFORMATDESCRIPTOR pixelFormatDesc = {0}; + pixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pixelFormatDesc.nVersion = 1; + pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; + pixelFormatDesc.iPixelType = PFD_TYPE_RGBA; + pixelFormatDesc.cColorBits = 32; + pixelFormatDesc.cAlphaBits = 8; + pixelFormatDesc.cDepthBits = 24; - int pixelFormat = ChoosePixelFormat(global_hdc, &pixelFormatDesc); - if (!pixelFormat) { - LogFail("ChoosePixelFormat", GetLastError()); - return -1; - } - if (!SetPixelFormat(global_hdc, pixelFormat, &pixelFormatDesc)) { - LogFail("SetPixelFormat", GetLastError()); - return -1; - } - global_gl_context = wglCreateContext(global_hdc); - if (!global_gl_context) { - LogFail("wglCreateContext", GetLastError()); - return -1; - } - if (!wglMakeCurrent(global_hdc, global_gl_context)) { - LogFail("wglMakeCurrent", GetLastError()); - return -1; - } + int pixelFormat = ChoosePixelFormat(globalHdc, &pixelFormatDesc); + if (!pixelFormat) + { + LogFail("ChoosePixelFormat", GetLastError()); + return -1; + } - rlLoadExtensions(WglGetProcAddress); + if (!SetPixelFormat(globalHdc, pixelFormat, &pixelFormatDesc)) + { + LogFail("SetPixelFormat", GetLastError()); + return -1; + } - global_hwnd = hwnd; - return 0; - } - case WM_DESTROY: - wglMakeCurrent(global_hdc, NULL); - if (global_gl_context) { - if (!wglDeleteContext(global_gl_context)) abort(); - global_gl_context = NULL; - } - if (global_hdc) { - if (!ReleaseDC(hwnd, global_hdc)) abort(); - global_hdc = NULL; - } - return 0; - case WM_CLOSE: - CORE.Window.shouldClose = true; - return 0; - case WM_KILLFOCUS: - memset(CORE.Input.Keyboard.previousKeyState, 0, sizeof(CORE.Input.Keyboard.previousKeyState)); - memset(CORE.Input.Keyboard.currentKeyState, 0, sizeof(CORE.Input.Keyboard.currentKeyState)); - return 0; - case WM_SIZING: { - if (CORE.Window.flags & FLAG_WINDOW_RESIZABLE) { - // TODO: enforce min/max size - TRACELOG(LOG_WARNING, "TODO: enforce min/max size!"); - } else { - TRACELOG(LOG_WARNING, "non-resizable window is being resized"); - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - abort(); // TODO - } - return TRUE; - } - case WM_STYLECHANGING: - if (wparam == GWL_STYLE) { - STYLESTRUCT* ss = (STYLESTRUCT*)lparam; - GetStyleChangeFlagOps(CORE.Window.flags, ss, deferred_flags); + globalGlContext = wglCreateContext(globalHdc); + if (!globalGlContext) + { + LogFail("wglCreateContext", GetLastError()); + return -1; + } - UINT dpi = GetWindowDpi(hwnd); - SIZE client_size = GetClientSize(hwnd); - SIZE old_size = CalcWindowSize(dpi, client_size, ss->styleOld); - SIZE new_size = CalcWindowSize(dpi, client_size, ss->styleNew); - if (old_size.cx != new_size.cx || old_size.cy != new_size.cy) { - TRACELOG( - LOG_INFO, - "resize from style change: %dx%d to %dx%d", - old_size.cx, old_size.cy, - new_size.cx, new_size.cy - ); - if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) { - // looks like windows will automatically "unminimize" a window - // if a style changes modifies it's size - TRACELOG(LOG_INFO, "style change modifed window size, removing maximized flag"); - deferred_flags->clear |= FLAG_WINDOW_MAXIMIZED; + if (!wglMakeCurrent(globalHdc, globalGlContext)) + { + LogFail("wglMakeCurrent", GetLastError()); + return -1; + } + + rlLoadExtensions(WglGetProcAddress); + + globalHwnd = hwnd; + + } return 0; + case WM_DESTROY: + wglMakeCurrent(globalHdc, NULL); + if (globalGlContext) + { + if (!wglDeleteContext(globalGlContext)) abort(); + globalGlContext = NULL; + } + + if (globalHdc) + { + if (!ReleaseDC(hwnd, globalHdc)) abort(); + globalHdc = NULL; + } + + return 0; + case WM_CLOSE: + CORE.Window.shouldClose = true; + return 0; + case WM_KILLFOCUS: + memset(CORE.Input.Keyboard.previousKeyState, 0, sizeof(CORE.Input.Keyboard.previousKeyState)); + memset(CORE.Input.Keyboard.currentKeyState, 0, sizeof(CORE.Input.Keyboard.currentKeyState)); + return 0; + case WM_SIZING: + if (CORE.Window.flags & FLAG_WINDOW_RESIZABLE) { + // TODO: enforce min/max size + TRACELOG(LOG_WARNING, "TODO: enforce min/max size!"); + } else { + TRACELOG(LOG_WARNING, "non-resizable window is being resized"); + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + abort(); // TODO + } + return TRUE; + case WM_STYLECHANGING: + if (wparam == GWL_STYLE) + { + STYLESTRUCT *ss = (STYLESTRUCT*)lparam; + GetStyleChangeFlagOps(CORE.Window.flags, ss, deferredFlags); + + UINT dpi = GetWindowDpi(hwnd); + SIZE clientSize = GetClientSize(hwnd); + SIZE oldSize = CalcWindowSize(dpi, clientSize, ss->styleOld); + SIZE newSize = CalcWindowSize(dpi, clientSize, ss->styleNew); + if (oldSize.cx != newSize.cx || oldSize.cy != newSize.cy) { + TRACELOG( + LOG_INFO, + "resize from style change: %dx%d to %dx%d", + oldSize.cx, oldSize.cy, + newSize.cx, newSize.cy + ); + if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) { + // looks like windows will automatically "unminimize" a window + // if a style changes modifies it's size + TRACELOG(LOG_INFO, "style change modifed window size, removing maximized flag"); + deferredFlags->clear |= FLAG_WINDOW_MAXIMIZED; + } } } - } - return 0; - case WM_WINDOWPOSCHANGING: { - WINDOWPOS* pos = (WINDOWPOS*)lparam; - if (pos->flags & SWP_SHOWWINDOW) { - if (pos->flags & SWP_HIDEWINDOW) abort(); - deferred_flags->clear |= FLAG_WINDOW_HIDDEN; - } else if (pos->flags & SWP_HIDEWINDOW) { - deferred_flags->set |= FLAG_WINDOW_HIDDEN; - } + return 0; + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS *pos = (WINDOWPOS*)lparam; + if (pos->flags & SWP_SHOWWINDOW) + { + if (pos->flags & SWP_HIDEWINDOW) abort(); + deferredFlags->clear |= FLAG_WINDOW_HIDDEN; + } + else if (pos->flags & SWP_HIDEWINDOW) + { + deferredFlags->set |= FLAG_WINDOW_HIDDEN; + } - Mized mized = MIZED_NONE; - if (IsMinimized2(hwnd)) { - mized = MIZED_MIN; - } else { - WINDOWPLACEMENT placement; - placement.length = sizeof(placement); - if (!GetWindowPlacement(hwnd, &placement)) { - LogFail("GetWindowPlacement", GetLastError()); + Mized mized = MIZED_NONE; + if (IsMinimized2(hwnd)) + { + mized = MIZED_MIN; + } + else + { + WINDOWPLACEMENT placement; + placement.length = sizeof(placement); + if (!GetWindowPlacement(hwnd, &placement)) + { + LogFail("GetWindowPlacement", GetLastError()); + abort(); + } + + if (placement.showCmd == SW_SHOWMAXIMIZED) { + mized = MIZED_MAX; + } + } + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + /* TRACELOG(LOG_INFO, "window pos=%d,%d size=%dx%d", pos->x, pos->y, pos->cx, pos->cy); */ + + switch (mized) + { + case MIZED_NONE: + { + deferredFlags->clear |= ( + FLAG_WINDOW_MINIMIZED | + FLAG_WINDOW_MAXIMIZED + ); + HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO info; + info.cbSize = sizeof(info); + if (!GetMonitorInfoW(monitor, &info)) + { + LogFail("GetMonitorInfo", GetLastError()); + abort(); + } + + if ( + (pos->x == info.rcMonitor.left) && + (pos->y == info.rcMonitor.top) && + (pos->cx == (info.rcMonitor.right - info.rcMonitor.left)) && + (pos->cy == (info.rcMonitor.bottom - info.rcMonitor.top)) + ) { + deferredFlags->set |= FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + deferredFlags->clear |= FLAG_BORDERLESS_WINDOWED_MODE; + } + + } break; + case MIZED_MIN: + // !!! NOTE !!! Do not update the maximized/borderless + // flags because when hwnd is minimized it temporarily overrides + // the maximized state/flag which gets restored on SW_RESTORE + deferredFlags->set |= FLAG_WINDOW_MINIMIZED; + break; + case MIZED_MAX: + deferredFlags->clear |= FLAG_WINDOW_MINIMIZED; + deferredFlags->set |= FLAG_WINDOW_MAXIMIZED; + break; + } + + return 0; + } + case WM_SIZE: + // don't trust the docs, they say you won't get this message if you don't call DefWindowProc + // in response to WM_WINDOWPOSCHANGED but looks like when a window is created you'll get this + // message without getting WM_WINDOWPOSCHANGED. + HandleWindowResize(hwnd, &globalAppScreenSize); + return 0; + case WM_WINDOWPOSCHANGED: { + WINDOWPOS *pos = (WINDOWPOS*)lparam; + if (!(pos->flags & SWP_NOSIZE)) + { + HandleWindowResize(hwnd, &globalAppScreenSize); + } + + return 0; + } + case WM_GETDPISCALEDSIZE: + { + SIZE *inoutSize = (SIZE*)lparam; + UINT newDpi = wparam; + + // for any of these other cases, we might want to post a window + // resize event after the dpi changes? + if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return TRUE; + if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) return TRUE; + if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) return TRUE; + + float dpiScale = ScaleFromDpi(newDpi); + bool dpiScaling = CORE.Window.flags & FLAG_WINDOW_HIGHDPI; + SIZE desired = PxFromPt2(dpiScale, dpiScaling, globalAppScreenSize); + inoutSize->cx = desired.cx; + inoutSize->cy = desired.cy; + } return TRUE; + case WM_DPICHANGED: + { + RECT *suggestedRect = (RECT*)lparam; + // Never set the window size to anything other than the suggested rect here. + // Doing so can cause a window to stutter between monitors when transitioning + // between them. + if (!SetWindowPos( + hwnd, + NULL, + suggestedRect->left, + suggestedRect->top, + suggestedRect->right - suggestedRect->left, + suggestedRect->bottom - suggestedRect->top, + SWP_NOZORDER | SWP_NOACTIVATE + )) { + LogFail("SetWindowPos", GetLastError()); abort(); } - if (placement.showCmd == SW_SHOWMAXIMIZED) { - mized = MIZED_MAX; + } return 0; + case WM_SETCURSOR: + if (LOWORD(lparam) == HTCLIENT) + { + SetCursor(CORE.Input.Mouse.cursorHidden? NULL : LoadCursorW(NULL, IDC_ARROW)); + return 0; } - } - - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - /* TRACELOG(LOG_INFO, "window pos=%d,%d size=%dx%d", pos->x, pos->y, pos->cx, pos->cy); */ - - switch (mized) { - case MIZED_NONE: - deferred_flags->clear |= ( - FLAG_WINDOW_MINIMIZED | - FLAG_WINDOW_MAXIMIZED - ); - HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); - MONITORINFO info; - info.cbSize = sizeof(info); - if (!GetMonitorInfoW(monitor, &info)) { - LogFail("GetMonitorInfo", GetLastError()); + return DefWindowProc(hwnd, msg, wparam, lparam); + case WM_INPUT: + if (globalCursorEnabled) + { + TRACELOG(LOG_ERROR, "Unexpected raw mouse input"); // impossible right? abort(); } - if ( - (pos->x == info.rcMonitor.left) && - (pos->y == info.rcMonitor.top) && - (pos->cx == (info.rcMonitor.right - info.rcMonitor.left)) && - (pos->cy == (info.rcMonitor.bottom - info.rcMonitor.top)) - ) { - deferred_flags->set |= FLAG_BORDERLESS_WINDOWED_MODE; - } else { - deferred_flags->clear |= FLAG_BORDERLESS_WINDOWED_MODE; + HandleRawInput(lparam); + return 0; + case WM_MOUSEMOVE: + if (globalCursorEnabled) + { + CORE.Input.Mouse.currentPosition.x = (float)GET_X_LPARAM(lparam); + CORE.Input.Mouse.currentPosition.y = (float)GET_Y_LPARAM(lparam); + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; } - break; - case MIZED_MIN: - // !!! NOTE !!! Do not update the maximized/borderless - // flags because when hwnd is minimized it temporarily overrides - // the maximized state/flag which gets restored on SW_RESTORE - deferred_flags->set |= FLAG_WINDOW_MINIMIZED; - break; - case MIZED_MAX: - deferred_flags->clear |= FLAG_WINDOW_MINIMIZED; - deferred_flags->set |= FLAG_WINDOW_MAXIMIZED; - break; - } - - return 0; - } - case WM_SIZE: - // don't trust the docs, they say you won't get this message if you don't call DefWindowProc - // in response to WM_WINDOWPOSCHANGED but looks like when a window is created you'll get this - // message without getting WM_WINDOWPOSCHANGED. - HandleWindowResize(hwnd, &global_app_screen_size); - return 0; - case WM_WINDOWPOSCHANGED: { - WINDOWPOS* pos = (WINDOWPOS*)lparam; - if (!(pos->flags & SWP_NOSIZE)) { - HandleWindowResize(hwnd, &global_app_screen_size); - } - return 0; - } - case WM_GETDPISCALEDSIZE: { - SIZE *inout_size = (SIZE*)lparam; - UINT new_dpi = wparam; - - // for any of these other cases, we might want to post a window - // resize event after the dpi changes? - if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return TRUE; - if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) return TRUE; - if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) return TRUE; - - float dpi_scale = ScaleFromDpi(new_dpi); - bool dpi_scaling = CORE.Window.flags & FLAG_WINDOW_HIGHDPI; - SIZE desired = PxFromPt2(dpi_scale, dpi_scaling, global_app_screen_size); - inout_size->cx = desired.cx; - inout_size->cy = desired.cy; - return TRUE; - } - case WM_DPICHANGED: { - RECT *suggested_rect = (RECT*)lparam; - // Never set the window size to anything other than the suggested rect here. - // Doing so can cause a window to stutter between monitors when transitioning - // between them. - if (!SetWindowPos( - hwnd, - NULL, - suggested_rect->left, - suggested_rect->top, - suggested_rect->right - suggested_rect->left, - suggested_rect->bottom - suggested_rect->top, - SWP_NOZORDER | SWP_NOACTIVATE - )) { - LogFail("SetWindowPos", GetLastError()); - abort(); - } - return 0; - } - case WM_SETCURSOR: - if (LOWORD(lparam) == HTCLIENT) { - SetCursor(CORE.Input.Mouse.cursorHidden ? NULL : LoadCursorW(NULL, IDC_ARROW)); return 0; + case WM_KEYDOWN: HandleKey(wparam, lparam, 1); return 0; + case WM_KEYUP: HandleKey(wparam, lparam, 0); return 0; + case WM_LBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_LEFT, 1); return 0; + case WM_LBUTTONUP : HandleMouseButton(MOUSE_BUTTON_LEFT, 0); return 0; + case WM_RBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_RIGHT, 1); return 0; + case WM_RBUTTONUP : HandleMouseButton(MOUSE_BUTTON_RIGHT, 0); return 0; + case WM_MBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_MIDDLE, 1); return 0; + case WM_MBUTTONUP : HandleMouseButton(MOUSE_BUTTON_MIDDLE, 0); return 0; + case WM_XBUTTONDOWN: switch (HIWORD(wparam)) + { + case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 1); return 0; + case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 1); return 0; + default: + TRACELOG(LOG_WARNING, "TODO: handle ex mouse button DOWN wparam=%u", HIWORD(wparam)); + return 0; } - return DefWindowProc(hwnd, msg, wparam, lparam); - case WM_INPUT: - if (global_cursor_enabled) { - TRACELOG(LOG_ERROR, "Unexpected raw mouse input"); // impossible right? - abort(); + case WM_XBUTTONUP: switch (HIWORD(wparam)) + { + case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 0); return 0; + case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 0); return 0; + default: + TRACELOG(LOG_WARNING, "TODO: handle ex mouse button UP wparam=%u", HIWORD(wparam)); + return 0; } - HandleRawInput(lparam); - return 0; - case WM_MOUSEMOVE: - if (global_cursor_enabled) { - CORE.Input.Mouse.currentPosition.x = (float)GET_X_LPARAM(lparam); - CORE.Input.Mouse.currentPosition.y = (float)GET_Y_LPARAM(lparam); - CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; - } - return 0; - case WM_KEYDOWN: HandleKey(wparam, lparam, 1); return 0; - case WM_KEYUP: HandleKey(wparam, lparam, 0); return 0; - case WM_LBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_LEFT, 1); return 0; - case WM_LBUTTONUP : HandleMouseButton(MOUSE_BUTTON_LEFT, 0); return 0; - case WM_RBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_RIGHT, 1); return 0; - case WM_RBUTTONUP : HandleMouseButton(MOUSE_BUTTON_RIGHT, 0); return 0; - case WM_MBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_MIDDLE, 1); return 0; - case WM_MBUTTONUP : HandleMouseButton(MOUSE_BUTTON_MIDDLE, 0); return 0; - case WM_XBUTTONDOWN: switch (HIWORD(wparam)) { - case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 1); return 0; - case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 1); return 0; + case WM_MOUSEWHEEL: + CORE.Input.Mouse.currentWheelMove.y = ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; + return 0; + case WM_MOUSEHWHEEL: + CORE.Input.Mouse.currentWheelMove.x = ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; + return 0; + case WM_APP_UPDATE_WINDOW_SIZE: + UpdateWindowSize(UPDATE_WINDOW_NORMAL, hwnd, globalAppScreenSize, CORE.Window.flags); + return 0; default: - TRACELOG(LOG_WARNING, "TODO: handle ex mouse button DOWN wparam=%u", HIWORD(wparam)); - return 0; - } - case WM_XBUTTONUP: switch (HIWORD(wparam)) { - case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 0); return 0; - case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 0); return 0; - default: - TRACELOG(LOG_WARNING, "TODO: handle ex mouse button UP wparam=%u", HIWORD(wparam)); - return 0; - } - case WM_MOUSEWHEEL: - CORE.Input.Mouse.currentWheelMove.y = ((float)GET_WHEEL_DELTA_WPARAM(wparam)) / WHEEL_DELTA; - return 0; - case WM_MOUSEHWHEEL: - CORE.Input.Mouse.currentWheelMove.x = ((float)GET_WHEEL_DELTA_WPARAM(wparam)) / WHEEL_DELTA; - return 0; - case WM_APP_UPDATE_WINDOW_SIZE: - UpdateWindowSize(UPDATE_WINDOW_NORMAL, hwnd, global_app_screen_size, CORE.Window.flags); - return 0; - default: - return DefWindowProcW(hwnd, msg, wparam, lparam); + return DefWindowProcW(hwnd, msg, wparam, lparam); } } + static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // sanity check, should we remove this? DWORD mask = STYLE_MASK_ALL; - if (global_hwnd == hwnd) { + if (globalHwnd == hwnd) + { if (msg == WM_WINDOWPOSCHANGING) { mask &= ~(WS_MINIMIZE | WS_MAXIMIZE); } CheckFlags("WndProc", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), mask); } - FlagsOp flags_op; - flags_op.set = 0; - flags_op.clear = 0; - LRESULT result = WndProc2(hwnd, msg, wparam, lparam, &flags_op); + FlagsOp flagsOp; + flagsOp.set = 0; + flagsOp.clear = 0; + LRESULT result = WndProc2(hwnd, msg, wparam, lparam, &flagsOp); // sanity check, should we remove this? - if (global_hwnd == hwnd) { + if (globalHwnd == hwnd) + { CheckFlags("After WndProc", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), mask); } // Operations to execute after the above check - if (flags_op.set & flags_op.clear) { - TRACELOG(LOG_ERROR, "the flags 0x%x were both set and cleared!", flags_op.set & flags_op.clear); + if (flagsOp.set & flagsOp.clear) + { + TRACELOG(LOG_ERROR, "the flags 0x%x were both set and cleared!", flagsOp.set & flagsOp.clear); abort(); } + { DWORD save = CORE.Window.flags; - CORE.Window.flags |= flags_op.set; - CORE.Window.flags &= ~flags_op.clear; - if (save != CORE.Window.flags) { + CORE.Window.flags |= flagsOp.set; + CORE.Window.flags &= ~flagsOp.clear; + if (save != CORE.Window.flags) + { TRACELOG( LOG_DEBUG, "DeferredFlags: 0x%x > 0x%x (diff 0x%x)", @@ -1867,25 +2043,29 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara ); } } + return result; } int InitPlatform(void) { - global_desired_flags = SanitizeFlags(SANITIZE_FLAGS_FIRST, CORE.Window.flags); + globalDesiredFlags = SanitizeFlags(SANITIZE_FLAGS_FIRST, CORE.Window.flags); // from this point CORE.Window.flags should always reflect the actual state of the window - CORE.Window.flags = FLAG_WINDOW_HIDDEN | (global_desired_flags & FLAG_MASK_NO_UPDATE); + CORE.Window.flags = FLAG_WINDOW_HIDDEN | (globalDesiredFlags & FLAG_MASK_NO_UPDATE); - global_app_screen_size = (Vector2){ CORE.Window.screen.width, CORE.Window.screen.height }; + globalAppScreenSize = (Vector2){ CORE.Window.screen.width, CORE.Window.screen.height }; - if (IsWindows10Version1703OrGreaterWin32()) { + if (IsWindows10Version1703OrGreaterWin32()) + { TRACELOG(LOG_INFO, "DpiAware: >=Win10Creators"); if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) { LogFail("SetProcessDpiAwarenessContext", GetLastError()); abort(); } - } else if (IsWindows8Point1OrGreater()) { + } + else if (IsWindows8Point1OrGreater()) + { TRACELOG(LOG_INFO, "DpiAware: >=Win8.1"); HMODULE shcore = LoadLibraryW(L"shcore.dll"); if (!shcore) { @@ -1902,10 +2082,14 @@ int InitPlatform(void) LogFailHr("SetProcessDpiAwareness", hr); abort(); } - } else if (IsWindowsVistaOrGreater()) { + } + else if (IsWindowsVistaOrGreater()) + { TRACELOG(LOG_INFO, "DpiAware: >=WinVista"); SetProcessDPIAware(); - } else { + } + else + { TRACELOG(LOG_INFO, "DpiAware: Date: Fri, 18 Apr 2025 09:03:04 -0600 Subject: [PATCH 07/10] [rcore][win32] workaround unused function error SetupFramebuffer --- src/platforms/rcore_desktop_win32.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 6a7a79e5c..5be13c73a 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -977,6 +977,11 @@ static void HandleWindowResize(HWND hwnd, Vector2 *appScreenSizeRef) return; SIZE clientSize = GetClientSize(hwnd); + + // TODO: not sure if this function is doing what we need, leaving this disabled + // call to workaround unused function error + if (0) SetupFramebuffer(0, 0); + TRACELOG(LOG_DEBUG, "NewClientSize %lux%lu", clientSize.cx, clientSize.cy); /* CORE.Window.currentFbo.width = clientSize.cx; */ /* CORE.Window.currentFbo.height = clientSize.cy; */ From 282d205ac12694aaac2a990c527707dce9a849ef Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 18 Apr 2025 09:43:42 -0600 Subject: [PATCH 08/10] [rcore][win32] re-enable sanitize flags check for MSAA_4X --- src/platforms/rcore_desktop_win32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 5be13c73a..ff3045519 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -1068,7 +1068,7 @@ static unsigned SanitizeFlags(SanitizeFlagsKind kind, unsigned flags) switch (kind) { case SANITIZE_FLAGS_FIRST: break; - case SANITIZE_FLAGS_NORMAL: break; + case SANITIZE_FLAGS_NORMAL: if ((flags & FLAG_MSAA_4X_HINT) && (!(CORE.Window.flags & FLAG_MSAA_4X_HINT))) { TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); From 897d9c52b72d45afc09818fa6f823f73b722cfaa Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Fri, 18 Apr 2025 09:53:26 -0600 Subject: [PATCH 09/10] [rcore][win32] more style changes and remove old DPI cases Added some more missing spaces after conditional statements. Also made unsupported MSAA_4X an assert instead of an abort and also removed dpi-aware cases for older OS's. More changes would be needed to support those OS versions, namely, removing the dependency on shcore. --- src/platforms/rcore_desktop_win32.c | 38 ++++++++++------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index ff3045519..08f692517 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -322,8 +322,9 @@ static SIZE GetClientSize(HWND hwnd) LogFail("GetClientRect", GetLastError()); abort(); } - if (rect.left != 0) abort(); - if (rect.top != 0) abort(); + + if (rect.left != 0) abort(); // never happens AFAIK + if (rect.top != 0) abort(); // never happens AFAIK return (SIZE){ rect.right, rect.bottom }; } @@ -354,6 +355,7 @@ static SIZE CalcWindowSize(UINT dpi, SIZE clientSize, DWORD style) LogFail("AdjustWindowRect", GetLastError()); abort(); } + return (SIZE){ rect.right - rect.left, rect.bottom - rect.top }; } @@ -413,6 +415,7 @@ static bool UpdateWindowSize( LogFail("SetWindowPos", GetLastError()); abort(); } + return true; } @@ -1709,7 +1712,7 @@ static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, Flags if (CORE.Window.flags & FLAG_MSAA_4X_HINT) { TRACELOG(LOG_ERROR, "TODO: implement FLAG_MSAA_4X_HINT"); - abort(); + assert(0); } PIXELFORMATDESCRIPTOR pixelFormatDesc = {0}; @@ -1941,6 +1944,7 @@ static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, Flags LogFail("SetWindowPos", GetLastError()); abort(); } + } return 0; case WM_SETCURSOR: if (LOWORD(lparam) == HTCLIENT) @@ -1948,6 +1952,7 @@ static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, Flags SetCursor(CORE.Input.Mouse.cursorHidden? NULL : LoadCursorW(NULL, IDC_ARROW)); return 0; } + return DefWindowProc(hwnd, msg, wparam, lparam); case WM_INPUT: if (globalCursorEnabled) @@ -1955,6 +1960,7 @@ static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, Flags TRACELOG(LOG_ERROR, "Unexpected raw mouse input"); // impossible right? abort(); } + HandleRawInput(lparam); return 0; case WM_MOUSEMOVE: @@ -1964,6 +1970,7 @@ static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, Flags CORE.Input.Mouse.currentPosition.y = (float)GET_Y_LPARAM(lparam); CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; } + return 0; case WM_KEYDOWN: HandleKey(wparam, lparam, 1); return 0; case WM_KEYUP: HandleKey(wparam, lparam, 0); return 0; @@ -2069,34 +2076,15 @@ int InitPlatform(void) abort(); } } - else if (IsWindows8Point1OrGreater()) + else { - TRACELOG(LOG_INFO, "DpiAware: >=Win8.1"); - HMODULE shcore = LoadLibraryW(L"shcore.dll"); - if (!shcore) { - LogFail("LoadLibary shcore.dll", GetLastError()); - abort(); - } - HRESULT (*Set)(int) = (HRESULT(*)(int))GetProcAddress(shcore, "SetProcessDpiAwareness"); - if (!Set) { - LogFail("GetProcessAddress SetProcessDpiAwareness", GetLastError()); - abort(); - } - HRESULT hr = (*Set)(PROCESS_PER_MONITOR_DPI_AWARE); + TRACELOG(LOG_INFO, "DpiAware: =WinVista"); - SetProcessDPIAware(); - } - else - { - TRACELOG(LOG_INFO, "DpiAware: Date: Sun, 20 Apr 2025 08:32:22 -0600 Subject: [PATCH 10/10] [rcore][win32] fixes for compling with w64devkit without -DUNICODE --- src/platforms/rcore_desktop_win32.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/platforms/rcore_desktop_win32.c b/src/platforms/rcore_desktop_win32.c index 08f692517..a79fa692e 100644 --- a/src/platforms/rcore_desktop_win32.c +++ b/src/platforms/rcore_desktop_win32.c @@ -746,17 +746,17 @@ static LPCWSTR GetCursorName(int cursor) { switch (cursor) { - case MOUSE_CURSOR_DEFAULT : return IDC_ARROW; - case MOUSE_CURSOR_ARROW : return IDC_ARROW; - case MOUSE_CURSOR_IBEAM : return IDC_IBEAM; - case MOUSE_CURSOR_CROSSHAIR : return IDC_CROSS; - case MOUSE_CURSOR_POINTING_HAND: return IDC_HAND; - case MOUSE_CURSOR_RESIZE_EW : return IDC_SIZEWE; - case MOUSE_CURSOR_RESIZE_NS : return IDC_SIZENS; - case MOUSE_CURSOR_RESIZE_NWSE : return IDC_SIZENWSE; - case MOUSE_CURSOR_RESIZE_NESW : return IDC_SIZENESW; - case MOUSE_CURSOR_RESIZE_ALL : return IDC_SIZEALL; - case MOUSE_CURSOR_NOT_ALLOWED : return IDC_NO; + case MOUSE_CURSOR_DEFAULT : return (LPCWSTR)IDC_ARROW; + case MOUSE_CURSOR_ARROW : return (LPCWSTR)IDC_ARROW; + case MOUSE_CURSOR_IBEAM : return (LPCWSTR)IDC_IBEAM; + case MOUSE_CURSOR_CROSSHAIR : return (LPCWSTR)IDC_CROSS; + case MOUSE_CURSOR_POINTING_HAND: return (LPCWSTR)IDC_HAND; + case MOUSE_CURSOR_RESIZE_EW : return (LPCWSTR)IDC_SIZEWE; + case MOUSE_CURSOR_RESIZE_NS : return (LPCWSTR)IDC_SIZENS; + case MOUSE_CURSOR_RESIZE_NWSE : return (LPCWSTR)IDC_SIZENWSE; + case MOUSE_CURSOR_RESIZE_NESW : return (LPCWSTR)IDC_SIZENESW; + case MOUSE_CURSOR_RESIZE_ALL : return (LPCWSTR)IDC_SIZEALL; + case MOUSE_CURSOR_NOT_ALLOWED : return (LPCWSTR)IDC_NO; default: abort(); } } @@ -1441,7 +1441,7 @@ Image GetClipboardImage(void) void ShowCursor(void) { CORE.Input.Mouse.cursorHidden = false; - SetCursor(LoadCursorW(NULL, IDC_ARROW)); + SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_ARROW)); } // Hides mouse cursor @@ -1949,7 +1949,7 @@ static LRESULT WndProc2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, Flags case WM_SETCURSOR: if (LOWORD(lparam) == HTCLIENT) { - SetCursor(CORE.Input.Mouse.cursorHidden? NULL : LoadCursorW(NULL, IDC_ARROW)); + SetCursor(CORE.Input.Mouse.cursorHidden? NULL : LoadCursorW(NULL, (LPCWSTR)IDC_ARROW)); return 0; } @@ -2094,7 +2094,7 @@ int InitPlatform(void) c.cbWndExtra = sizeof(LONG_PTR); // extra space for the Tuple object ptr c.hInstance = GetModuleHandleW(0); // TODO: audit if we want to set this since we're implementing WM_SETCURSOR - c.hCursor = LoadCursorW(NULL, IDC_ARROW); + c.hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); c.lpszClassName = CLASS_NAME; if (0 == RegisterClassExW(&c)) {