Logo

index : raylib-jai

Bindings from https://solarium.technology

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/raylib-jai.git/html/Raylib/raylib/src/platforms/rcore_desktop_win32.c blob: 973fafa6874f9708b030a4b6922f4ba2374fbc8f [raw] [clear marker]

        
0/**********************************************************************************************
1*
2* rcore_desktop_win32 - Functions to manage window, graphics device and inputs
3*
4* PLATFORM: DESKTOP: WIN32
5* - Windows (Win32, Win64)
6*
7* LIMITATIONS:
8* - Initial development stage, lot of functionality missing
9* - No support for MOUSE_BUTTON_FORWARD/MOUSE_BUTTON_BACK
10*
11* POSSIBLE IMPROVEMENTS:
12* - Improvement 01
13* - Improvement 02
14*
15* ADDITIONAL NOTES:
16* - TRACELOG() function is located in raylib [utils] module
17*
18* CONFIGURATION:
19* #define RCORE_PLATFORM_CUSTOM_FLAG
20* Custom flag for rcore on target platform -not used-
21*
22* DEPENDENCIES:
23* - Win32 API (windows.h)
24*
25*
26* LICENSE: zlib/libpng
27*
28* Copyright (c) 2013-2025 Ramon Santamaria (@raysan5) and contributors
29*
30* This software is provided "as-is", without any express or implied warranty. In no event
31* will the authors be held liable for any damages arising from the use of this software.
32*
33* Permission is granted to anyone to use this software for any purpose, including commercial
34* applications, and to alter it and redistribute it freely, subject to the following restrictions:
35*
36* 1. The origin of this software must not be misrepresented; you must not claim that you
37* wrote the original software. If you use this software in a product, an acknowledgment
38* in the product documentation would be appreciated but is not required.
39*
40* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
41* as being the original software.
42*
43* 3. This notice may not be removed or altered from any source distribution.
44*
45**********************************************************************************************/
46
47// Move windows.h symbols to new names to avoid redefining the same names as raylib
48#define CloseWindow CloseWindowWin32
49#define Rectangle RectangleWin32
50#define ShowCursor ShowCursorWin32
51#define DrawTextA DrawTextAWin32
52#define DrawTextW DrawTextWin32
53#define DrawTextExA DrawTextExAWin32
54#define DrawTextExW DrawTextExWin32
55
56#define WIN32_LEAN_AND_MEAN
57#include <windows.h>
58
59#undef CloseWindow // raylib symbol collision
60#undef Rectangle // raylib symbol collision
61#undef ShowCursor // raylib symbol collision
62#undef LoadImage // raylib symbol collision
63#undef DrawText // raylib symbol collision
64#undef DrawTextA
65#undef DrawTextW
66#undef DrawTextEx // raylib symbol collision
67#undef DrawTextExA
68#undef DrawTextExW
69
70#include <windowsx.h>
71#include <shellscalingapi.h>
72#include <versionhelpers.h>
73
74#include <malloc.h> // Required for alloca()
75
76#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
77 #include <GL/gl.h>
78#endif
79
80//----------------------------------------------------------------------------------
81// Types and Structures Definition
82//----------------------------------------------------------------------------------
83
84// NOTE: appScreenSize is the last screen size requested by the app,
85// the backend must keep the client area this size (after DPI scaling is applied)
86// when the window isn't fullscreen/maximized/minimized
87typedef struct {
88 HWND hwnd; // Window handler
89 HDC hdc; // Graphic context handler
90 HGLRC glContext; // OpenGL context handler
91
92 // Software renderer variables
93 HDC hdcmem; // Memory graphic context handler
94 HBITMAP hbitmap; // GDI bitmap handler
95 unsigned int *pixels; // Pointer to pixel data buffer (BGRA format)
96
97 unsigned int appScreenWidth;
98 unsigned int appScreenHeight;
99 unsigned int desiredFlags;
100
101 LARGE_INTEGER timerFrequency;
102} PlatformData;
103
104// Define WGL function pointer types (no wglext.h needed)
105typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *);
106typedef BOOL (WINAPI *PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC, const int *, const FLOAT *, UINT, int *, UINT *);
107typedef BOOL (WINAPI *PFNWGLSWAPINTERVALEXTPROC)(int);
108typedef const char *(WINAPI *PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc);
109
110//----------------------------------------------------------------------------------
111// Global Variables Definition
112//----------------------------------------------------------------------------------
113extern CoreData CORE; // Global CORE state context
114
115static PlatformData platform = { 0 }; // Platform specific data
116
117// Required WGL functions
118static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
119static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
120static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
121static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = NULL;
122
123// --------------------------------------------------------------------------------
124// This part of the file contains pure functions that never access global state
125// This distinction helps keep the backend maintainable as the inputs and outputs
126// of every function called in this section can be fully derived from the call-site alone
127// --------------------------------------------------------------------------------
128
129// Prevent any code in this part of the file from accessing the global CORE state
130#define CORE DONT_USE_CORE_HERE
131
132//----------------------------------------------------------------------------------
133// Defines and Macros
134//----------------------------------------------------------------------------------
135#define A_TO_W_ALLOCA(outWstr, inAnsi) do { \
136 size_t outLen = AToWLen(inAnsi); \
137 outWstr = (WCHAR *)alloca(sizeof(WCHAR)*(outLen + 1)); \
138 AToWCopy(inAnsi, outWstr, outLen); \
139 outWstr[outLen] = 0; \
140} while (0)
141
142#define STYLE_MASK_ALL 0xffffffff
143#define STYLE_MASK_READONLY (WS_MINIMIZE | WS_MAXIMIZE)
144#define STYLE_MASK_WRITABLE (~STYLE_MASK_READONLY)
145
146#define STYLE_FLAGS_RESIZABLE WS_THICKFRAME
147
148#define STYLE_FLAGS_UNDECORATED_OFF (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX)
149#define STYLE_FLAGS_UNDECORATED_ON WS_POPUP
150
151#define WINDOW_STYLE_EX 0
152
153#define CLASS_NAME L"raylibWindow"
154
155#define FLAG_MASK_OPTIONAL (FLAG_VSYNC_HINT)
156#define FLAG_MASK_REQUIRED ~(FLAG_MASK_OPTIONAL)
157
158// Flags that have no operations to perform during an update
159#define FLAG_MASK_NO_UPDATE (FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT)
160
161#define WM_APP_UPDATE_WINDOW_SIZE (WM_APP + 1)
162
163#define WGL_DRAW_TO_WINDOW_ARB 0x2001
164#define WGL_ACCELERATION_ARB 0x2003
165#define WGL_SUPPORT_OPENGL_ARB 0x2010
166#define WGL_DOUBLE_BUFFER_ARB 0x2011
167#define WGL_PIXEL_TYPE_ARB 0x2013
168#define WGL_COLOR_BITS_ARB 0x2014
169#define WGL_RED_BITS_ARB 0x2015
170#define WGL_RED_SHIFT_ARB 0x2016
171#define WGL_GREEN_BITS_ARB 0x2017
172#define WGL_GREEN_SHIFT_ARB 0x2018
173#define WGL_BLUE_BITS_ARB 0x2019
174#define WGL_BLUE_SHIFT_ARB 0x201a
175#define WGL_ALPHA_BITS_ARB 0x201b
176#define WGL_ALPHA_SHIFT_ARB 0x201c
177#define WGL_DEPTH_BITS_ARB 0x2022
178#define WGL_STENCIL_BITS_ARB 0x2023
179#define WGL_TYPE_RGBA_ARB 0x202b
180
181// Context acceleration types
182#define WGL_NO_ACCELERATION_ARB 0x2025 // OpenGL 1.1 GDI software rasterizer
183#define WGL_GENERIC_ACCELERATION_ARB 0x2026
184#define WGL_FULL_ACCELERATION_ARB 0x2027 // OpenGL hardware-accelerated, using GPU-drivers provided by vendor
185
186// WGL_ARB_multisample extension supported
187#define WGL_SAMPLE_BUFFERS_ARB 0x2041 // Multisampling: 1 if multisample buffers are supported
188#define WGL_SAMPLES_ARB 0x2042 // Multisampling: Number of samples per pixel (4, 8, 16)
189
190// WGL_ARB_framebuffer_sRGB extension supported
191#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 // GL_TRUE if the framebuffer can do sRGB conversion
192
193#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
194#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
195#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
196#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
197#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
198#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
199#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
200#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
201
202//----------------------------------------------------------------------------------
203// Types and Structures Definition
204//----------------------------------------------------------------------------------
205// Maximize-minimize request types
206typedef enum {
207 MIZED_NONE,
208 MIZED_MIN,
209 MIZED_MAX
210} Mized;
211
212// Flag operations
213// NOTE: Some ops need to be deferred
214typedef struct {
215 DWORD set;
216 DWORD clear;
217} FlagsOp;
218
219// Monitor info type
220typedef struct {
221 HMONITOR needle;
222 int index;
223 int matchIndex;
224 RECT rect;
225} MonitorInfo;
226
227//----------------------------------------------------------------------------------
228// Module Internal Functions Declaration
229//----------------------------------------------------------------------------------
230// Get ASCII to WCHAR length
231static size_t AToWLen(const char *ascii)
232{
233 int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, ascii, -1, NULL, 0);
234
235 if (sizeNeeded < 0) TRACELOG(LOG_ERROR, "WIN32: Failed to calculate wide length [ERROR: %u]", GetLastError());
236
237 return sizeNeeded;
238}
239
240// Copy ASCII to WCHAR string
241static void AToWCopy(const char *ascii, wchar_t *outPtr, size_t outLen)
242{
243 int size = MultiByteToWideChar(CP_UTF8, 0, ascii, -1, outPtr, (int)outLen);
244 if (size != outLen) TRACELOG(LOG_WARNING, "WIN32: Failed to convert %i UTF-8 chars to WCHAR, converted %i chars", outLen, size);
245}
246
247static bool DecoratedFromStyle(DWORD style)
248{
249 if (style & STYLE_FLAGS_UNDECORATED_ON)
250 {
251 if (style & STYLE_FLAGS_UNDECORATED_OFF) TRACELOG(LOG_ERROR, "WIN32: FLAGS: Style 0x%x has both undecorated on/off flags", style);
252 return false; // Not decorated
253 }
254
255 DWORD masked = (style & STYLE_FLAGS_UNDECORATED_OFF);
256 if (STYLE_FLAGS_UNDECORATED_OFF != masked) TRACELOG(LOG_ERROR, "WIN32: FLAGS: Style 0x%x is missing flags 0x%x", masked, masked ^ STYLE_FLAGS_UNDECORATED_OFF);
257
258 return true; // Decorated
259}
260
261// Get window style from required flags
262static DWORD MakeWindowStyle(unsigned flags)
263{
264 // Flag is not needed because there are no child windows,
265 // but supposedly it improves efficiency, plus, windows adds this
266 // flag automatically anyway so it keeps flags in sync with the OS
267 DWORD style = WS_CLIPSIBLINGS;
268
269 style |= (flags & FLAG_WINDOW_HIDDEN)? 0 : WS_VISIBLE;
270 style |= (flags & FLAG_WINDOW_RESIZABLE)? STYLE_FLAGS_RESIZABLE : 0;
271 style |= (flags & FLAG_WINDOW_UNDECORATED)? STYLE_FLAGS_UNDECORATED_ON : STYLE_FLAGS_UNDECORATED_OFF;
272
273 // Minimized takes precedence over maximized
274 int mized = MIZED_NONE;
275 if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)) mized = MIZED_MIN;
276 if (flags & FLAG_WINDOW_MAXIMIZED) mized = MIZED_MAX;
277
278 switch (mized)
279 {
280 case MIZED_NONE: break;
281 case MIZED_MIN: style |= WS_MINIMIZE; break;
282 case MIZED_MAX: style |= WS_MAXIMIZE; break;
283 default: break;
284 }
285
286 return style;
287}
288
289// Check flags state, enforces that the actual window/platform state is in sync with raylib's flags
290static void CheckFlags(const char *context, HWND hwnd, DWORD flags, DWORD expectedStyle, DWORD styleCheckMask)
291{
292 DWORD styleFromFlags = MakeWindowStyle(flags);
293 if ((styleFromFlags & styleCheckMask) != (expectedStyle & styleCheckMask))
294 {
295 TRACELOG(LOG_ERROR, "WIN32: FLAGS: %s: window flags (0x%x) produced style 0x%x which != expected 0x%x (diff=0x%x, mask=0x%x)",
296 context, flags, styleFromFlags & styleCheckMask, expectedStyle & styleCheckMask,
297 (styleFromFlags & styleCheckMask) ^ (expectedStyle & styleCheckMask), styleCheckMask);
298 }
299
300 SetLastError(0);
301 LONG actualStyle = (LONG)GetWindowLongPtrW(hwnd, GWL_STYLE);
302 if ((actualStyle & styleCheckMask) != (expectedStyle & styleCheckMask))
303 {
304 TRACELOG(LOG_ERROR, "WIN32: FLAGS: %s: expected style 0x%x but got 0x%x (diff=0x%x, mask=0x%x, lasterror=%lu)",
305 context, expectedStyle & styleCheckMask, actualStyle & styleCheckMask,
306 (expectedStyle & styleCheckMask) ^ (actualStyle & styleCheckMask),
307 styleCheckMask, GetLastError());
308 }
309
310 if (styleCheckMask & WS_MINIMIZE)
311 {
312 bool isIconic = IsIconic(hwnd);
313 bool styleMinimized = !!(WS_MINIMIZE & actualStyle);
314 if (isIconic != styleMinimized) TRACELOG(LOG_ERROR, "WIN32: FLAGS: IsIconic(%d) != WS_MINIMIZED(%d)", isIconic, styleMinimized);
315 }
316
317 if (styleCheckMask & WS_MAXIMIZE)
318 {
319 WINDOWPLACEMENT placement;
320 placement.length = sizeof(placement);
321 if (!GetWindowPlacement(hwnd, &placement))
322 {
323 TRACELOG(LOG_ERROR, "WIN32: FLAGS: %s failed, error=%lu", "GetWindowPlacement", GetLastError());
324 }
325 bool placementMaximized = (placement.showCmd == SW_SHOWMAXIMIZED);
326 bool styleMaximized = WS_MAXIMIZE & actualStyle;
327 if (placementMaximized != styleMaximized)
328 {
329 TRACELOG(LOG_ERROR, "WIN32: FLAGS: Maximized state desync, placement maximized=%d (showCmd=%lu) style maximized=%d",
330 placementMaximized, placement.showCmd, styleMaximized);
331 }
332 }
333}
334
335// Calculate window size (with borders, title-bar...) from desired client size (framebuffer size)
336static SIZE CalcWindowSize(UINT dpi, SIZE clientSize, DWORD style)
337{
338 RECT rect = { 0, 0, clientSize.cx, clientSize.cy };
339
340 int result = AdjustWindowRectExForDpi(&rect, style, 0, WINDOW_STYLE_EX, dpi);
341 if (result == 0) TRACELOG(LOG_ERROR, "WIN32: Failed to adjust window rect [ERROR: %lu]", GetLastError());
342
343 return (SIZE){ rect.right - rect.left, rect.bottom - rect.top };
344}
345
346// Update window size if required
347// NOTE: Returns true if the window size was updated, false otherwise
348static bool UpdateWindowSize(int mode, HWND hwnd, int width, int height, unsigned flags)
349{
350 if (flags & FLAG_WINDOW_MINIMIZED) return false;
351
352 if (flags & FLAG_WINDOW_MAXIMIZED)
353 {
354 CheckFlags("UpdateWindowSize(maximized)", hwnd, flags, MakeWindowStyle(flags), STYLE_MASK_ALL);
355 return false;
356 }
357
358 if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
359 {
360 MONITORINFO info = { 0 };
361 HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
362 info.cbSize = sizeof(info);
363 if (!GetMonitorInfoW(monitor, &info)) TRACELOG(LOG_ERROR, "WIN32: Failed to get monitor info [ERROR: %lu]", GetLastError());
364
365 RECT windowRect = { 0 };
366 if (!GetWindowRect(hwnd, &windowRect)) TRACELOG(LOG_ERROR, "WIN32: Failed to get window rect [ERROR: %lu]", GetLastError());
367
368 if ((windowRect.left == info.rcMonitor.left) &&
369 (windowRect.top == info.rcMonitor.top) &&
370 ((windowRect.right - windowRect.left) == (info.rcMonitor.right - info.rcMonitor.left)) &&
371 ((windowRect.bottom - windowRect.top) == (info.rcMonitor.bottom - info.rcMonitor.top))) return false;
372
373 if (!SetWindowPos(hwnd, HWND_TOP,
374 info.rcMonitor.left, info.rcMonitor.top,
375 info.rcMonitor.right - info.rcMonitor.left,
376 info.rcMonitor.bottom - info.rcMonitor.top,
377 SWP_NOOWNERZORDER))
378 {
379 TRACELOG(LOG_ERROR, "WIN32: Failed to set window position [ERROR: %lu]", GetLastError());
380 }
381
382 return true;
383 }
384
385 // Get size in pixels from points, considering high-dpi
386 UINT dpi = GetDpiForWindow(hwnd);
387 float dpiScale = ((float)dpi)/96.0f;
388 bool dpiScaling = flags & FLAG_WINDOW_HIGHDPI;
389 SIZE desiredSize = {
390 .cx = dpiScaling? (int)((float)width*dpiScale) : width,
391 .cy = dpiScaling? (int)((float)height*dpiScale) : height
392 };
393
394 // Get client size (framebuffer inside the window)
395 RECT rect = { 0 };
396 GetClientRect(hwnd, &rect);
397 SIZE clientSize = { rect.right, rect.bottom };
398
399 // If client size is alread desired size, no need to update
400 if ((clientSize.cx == desiredSize.cx) || (clientSize.cy == desiredSize.cy)) return false;
401
402 TRACELOG(LOG_INFO, "WIN32: Restoring client size from [%dx%d] to [%dx%d] (dpi:%lu dpiScaling:%d app:%ix%i)",
403 clientSize.cx, clientSize.cy, desiredSize.cx, desiredSize.cy, dpi, dpiScaling, width, height);
404
405 // Calculate window size from desired framebuffer size and window flags
406 SIZE windowSize = CalcWindowSize(dpi, desiredSize, MakeWindowStyle(flags));
407 POINT windowPos = { 0 };
408 UINT swpFlags = SWP_NOZORDER | SWP_FRAMECHANGED;
409
410 if (mode == 0) // UPDATE_WINDOW_FIRST
411 {
412 HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
413 if (!monitor) TRACELOG(LOG_ERROR, "WIN32: Failed to get monitor from window [ERROR: %lu]", GetLastError());
414
415 MONITORINFO info = { 0 };
416 info.cbSize = sizeof(info);
417 if (!GetMonitorInfoW(monitor, &info)) TRACELOG(LOG_ERROR, "WIN32: Failed to get monitor info [ERROR: %lu]", GetLastError());
418
419 #define MAX(a,b) (((a)>(b))? (a):(b))
420
421 LONG monitorWidth = info.rcMonitor.right - info.rcMonitor.left;
422 LONG monitorHeight = info.rcMonitor.bottom - info.rcMonitor.top;
423 windowPos = (POINT){
424 MAX(0, (monitorWidth - windowSize.cx)/2),
425 MAX(0, (monitorHeight - windowSize.cy)/2),
426 };
427 }
428 else swpFlags |= SWP_NOMOVE;
429
430 // WARNING: This code must be called after swInit() has been called, after InitPlatform() in [rcore]
431 //RECT rc = {0, 0, desired.cx, desired.cy};
432 //AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, 0);
433 //SetWindowPos(hwnd, NULL, windowPos.x, windowPos.y, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
434
435 return true;
436}
437
438// Verify if we are running in Windows 10 version 1703 (Creators Update)
439static BOOL IsWindows10Version1703OrGreaterWin32(void)
440{
441 HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
442
443 DWORD (*Verify)(RTL_OSVERSIONINFOEXW*, ULONG, ULONGLONG) =
444 (DWORD (*)(RTL_OSVERSIONINFOEXW*, ULONG, ULONGLONG))GetProcAddress(ntdll, "RtlVerifyVersionInfo");
445 if (!Verify)
446 {
447 TRACELOG(LOG_ERROR, "WIN32: Failed to verify Windows version [ERROR: %lu]", GetLastError());
448 return 0;
449 }
450
451 RTL_OSVERSIONINFOEXW osvi = { 0 };
452 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
453 osvi.dwMajorVersion = 10;
454 osvi.dwMinorVersion = 0;
455 osvi.dwBuildNumber = 15063; // Build 15063 corresponds to Windows 10 version 1703 (Creators Update)
456
457 DWORDLONG cond = 0;
458 VER_SET_CONDITION(cond, VER_MAJORVERSION, VER_GREATER_EQUAL);
459 VER_SET_CONDITION(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
460 VER_SET_CONDITION(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL);
461
462 return 0 == (*Verify)(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, cond);
463}
464
465// Get OpenGL function pointers
466static void *WglGetProcAddress(const char *procname)
467{
468 void *proc = (void *)wglGetProcAddress(procname);
469
470 if ((proc == NULL) ||
471 // NOTE: Some GPU drivers could return following
472 // invalid sentinel values instead of NULL
473 (proc == (void *)0x1) ||
474 (proc == (void *)0x2) ||
475 (proc == (void *)0x3) ||
476 (proc == (void *)-1))
477 {
478 // TODO: Keep gl module pointer as global platform data?
479 HMODULE glModule = LoadLibraryW(L"opengl32.dll");
480 proc = (void *)GetProcAddress(glModule, procname);
481
482 //if (proc == NULL) TRACELOG(LOG_ERROR, "GL: GetProcAddress() failed to get %s [%p], error=%u", procname, proc, GetLastError());
483 //else TRACELOG(LOG_INFO, "GL: Found entry point for %s [%p]", procname, proc);
484 }
485
486 return proc;
487}
488
489// Get key from wparam (mapping)
490static KeyboardKey GetKeyFromWparam(WPARAM wparam)
491{
492 switch (wparam)
493 {
494 /* case VK_LBUTTON: return KEY_; */
495 /* case VK_RBUTTON: return KEY_; */
496 /* case VK_CANCEL: return KEY_; */
497 /* case VK_MBUTTON: return KEY_; */
498 /* case VK_XBUTTON1: return KEY_; */
499 /* case VK_XBUTTON2: return KEY_; */
500 /* case VK_BACK: return KEY_; */
501 /* case VK_TAB: return KEY_; */
502 /* case VK_CLEAR: return KEY_; */
503 case VK_RETURN: return KEY_ENTER;
504 /* case VK_SHIFT: return KEY_; */
505 /* case VK_CONTROL: return KEY_; */
506 /* case VK_MENU: return KEY_; */
507 /* case VK_PAUSE: return KEY_; */
508 /* case VK_CAPITAL: return KEY_; */
509 /* case VK_KANA: return KEY_; */
510 /* case VK_HANGUL: return KEY_; */
511 /* case VK_IME_ON: return KEY_; */
512 /* case VK_JUNJA: return KEY_; */
513 /* case VK_FINAL: return KEY_; */
514 /* case VK_HANJA: return KEY_; */
515 /* case VK_KANJI: return KEY_; */
516 /* case VK_IME_OFF: return KEY_; */
517 case VK_ESCAPE: return KEY_ESCAPE;
518 /* case VK_CONVERT: return KEY_; */
519 /* case VK_NONCONVERT: return KEY_; */
520 /* case VK_ACCEPT: return KEY_; */
521 /* case VK_MODECHANGE: return KEY_; */
522 case VK_SPACE: return KEY_SPACE;
523 /* case VK_PRIOR: return KEY_; */
524 /* case VK_NEXT: return KEY_; */
525 /* case VK_END: return KEY_; */
526 /* case VK_HOME: return KEY_; */
527 case VK_LEFT: return KEY_LEFT;
528 case VK_UP: return KEY_UP;
529 case VK_RIGHT: return KEY_RIGHT;
530 case VK_DOWN: return KEY_DOWN;
531 /* case VK_SELECT: return KEY_; */
532 /* case VK_PRINT: return KEY_; */
533 /* case VK_EXECUTE: return KEY_; */
534 /* case VK_SNAPSHOT: return KEY_; */
535 /* case VK_INSERT: return KEY_; */
536 /* case VK_DELETE: return KEY_; */
537 /* case VK_HELP: return KEY_; */
538 case '0': return KEY_ZERO;
539 case '1': return KEY_ONE;
540 case '2': return KEY_TWO;
541 case '3': return KEY_THREE;
542 case '4': return KEY_FOUR;
543 case '5': return KEY_FIVE;
544 case '6': return KEY_SIX;
545 case '7': return KEY_SEVEN;
546 case '8': return KEY_EIGHT;
547 case '9': return KEY_NINE;
548 /* case 0x3A-40: return KEY_; */
549 case 'A': return KEY_A;
550 case 'B': return KEY_B;
551 case 'C': return KEY_C;
552 case 'D': return KEY_D;
553 case 'E': return KEY_E;
554 case 'F': return KEY_F;
555 case 'G': return KEY_G;
556 case 'H': return KEY_H;
557 case 'I': return KEY_I;
558 case 'J': return KEY_J;
559 case 'K': return KEY_K;
560 case 'L': return KEY_L;
561 case 'M': return KEY_M;
562 case 'N': return KEY_N;
563 case 'O': return KEY_O;
564 case 'P': return KEY_P;
565 case 'Q': return KEY_Q;
566 case 'R': return KEY_R;
567 case 'S': return KEY_S;
568 case 'T': return KEY_T;
569 case 'U': return KEY_U;
570 case 'V': return KEY_V;
571 case 'W': return KEY_W;
572 case 'X': return KEY_X;
573 case 'Y': return KEY_Y;
574 case 'Z': return KEY_Z;
575 /* case VK_LWIN: return KEY_; */
576 /* case VK_RWIN: return KEY_; */
577 /* case VK_APPS: return KEY_; */
578 /* case VK_SLEEP: return KEY_; */
579 /* case VK_NUMPAD0: return KEY_; */
580 /* case VK_NUMPAD1: return KEY_; */
581 /* case VK_NUMPAD2: return KEY_; */
582 /* case VK_NUMPAD3: return KEY_; */
583 /* case VK_NUMPAD4: return KEY_; */
584 /* case VK_NUMPAD5: return KEY_; */
585 /* case VK_NUMPAD6: return KEY_; */
586 /* case VK_NUMPAD7: return KEY_; */
587 /* case VK_NUMPAD8: return KEY_; */
588 /* case VK_NUMPAD9: return KEY_; */
589 /* case VK_MULTIPLY: return KEY_; */
590 /* case VK_ADD: return KEY_; */
591 /* case VK_SEPARATOR: return KEY_; */
592 /* case VK_SUBTRACT: return KEY_; */
593 /* case VK_DECIMAL: return KEY_; */
594 /* case VK_DIVIDE: return KEY_; */
595 /* case VK_F1: return KEY_; */
596 /* case VK_F2: return KEY_; */
597 /* case VK_F3: return KEY_; */
598 /* case VK_F4: return KEY_; */
599 /* case VK_F5: return KEY_; */
600 /* case VK_F6: return KEY_; */
601 /* case VK_F7: return KEY_; */
602 /* case VK_F8: return KEY_; */
603 /* case VK_F9: return KEY_; */
604 /* case VK_F10: return KEY_; */
605 /* case VK_F11: return KEY_; */
606 /* case VK_F12: return KEY_; */
607 /* case VK_F13: return KEY_; */
608 /* case VK_F14: return KEY_; */
609 /* case VK_F15: return KEY_; */
610 /* case VK_F16: return KEY_; */
611 /* case VK_F17: return KEY_; */
612 /* case VK_F18: return KEY_; */
613 /* case VK_F19: return KEY_; */
614 /* case VK_F20: return KEY_; */
615 /* case VK_F21: return KEY_; */
616 /* case VK_F22: return KEY_; */
617 /* case VK_F23: return KEY_; */
618 /* case VK_F24: return KEY_; */
619 /* case VK_NUMLOCK: return KEY_; */
620 /* case VK_SCROLL: return KEY_; */
621 /* case VK_LSHIFT: return KEY_; */
622 /* case VK_RSHIFT: return KEY_; */
623 /* case VK_LCONTROL: return KEY_; */
624 /* case VK_RCONTROL: return KEY_; */
625 /* case VK_LMENU: return KEY_; */
626 /* case VK_RMENU: return KEY_; */
627 /* case VK_BROWSER_BACK: return KEY_; */
628 /* case VK_BROWSER_FORWARD: return KEY_; */
629 /* case VK_BROWSER_REFRESH: return KEY_; */
630 /* case VK_BROWSER_STOP: return KEY_; */
631 /* case VK_BROWSER_SEARCH: return KEY_; */
632 /* case VK_BROWSER_FAVORITES: return KEY_; */
633 /* case VK_BROWSER_HOME: return KEY_; */
634 /* case VK_VOLUME_MUTE: return KEY_; */
635 /* case VK_VOLUME_DOWN: return KEY_; */
636 /* case VK_VOLUME_UP: return KEY_; */
637 /* case VK_MEDIA_NEXT_TRACK: return KEY_; */
638 /* case VK_MEDIA_PREV_TRACK: return KEY_; */
639 /* case VK_MEDIA_STOP: return KEY_; */
640 /* case VK_MEDIA_PLAY_PAUSE: return KEY_; */
641 /* case VK_LAUNCH_MAIL: return KEY_; */
642 /* case VK_LAUNCH_MEDIA_SELECT: return KEY_; */
643 /* case VK_LAUNCH_APP1: return KEY_; */
644 /* case VK_LAUNCH_APP2: return KEY_; */
645 /* case VK_OEM_1: return KEY_; */
646 /* case VK_OEM_PLUS: return KEY_; */
647 /* case VK_OEM_COMMA: return KEY_; */
648 /* case VK_OEM_MINUS: return KEY_; */
649 /* case VK_OEM_PERIOD: return KEY_; */
650 /* case VK_OEM_2: return KEY_; */
651 /* case VK_OEM_3: return KEY_; */
652 /* case VK_OEM_4: return KEY_; */
653 /* case VK_OEM_5: return KEY_; */
654 /* case VK_OEM_6: return KEY_; */
655 /* case VK_OEM_7: return KEY_; */
656 /* case VK_OEM_8: return KEY_; */
657 /* case VK_OEM_102: return KEY_; */
658 /* case VK_PROCESSKEY: return KEY_; */
659 /* case VK_PACKET: return KEY_; */
660 /* case VK_ATTN: return KEY_; */
661 /* case VK_CRSEL: return KEY_; */
662 /* case VK_EXSEL: return KEY_; */
663 /* case VK_EREOF: return KEY_; */
664 /* case VK_PLAY: return KEY_; */
665 /* case VK_ZOOM: return KEY_; */
666 /* case VK_NONAME: return KEY_; */
667 /* case VK_PA1: return KEY_; */
668 /* case VK_OEM_CLEAR: return KEY_; */
669 default: return KEY_NULL;
670 }
671}
672
673// Get cursor name
674static LPCWSTR GetCursorName(int cursor)
675{
676 LPCWSTR name = (LPCWSTR)IDC_ARROW;
677
678 switch (cursor)
679 {
680 case MOUSE_CURSOR_DEFAULT: name = (LPCWSTR)IDC_ARROW; break;
681 case MOUSE_CURSOR_ARROW: name = (LPCWSTR)IDC_ARROW; break;
682 case MOUSE_CURSOR_IBEAM: name = (LPCWSTR)IDC_IBEAM; break;
683 case MOUSE_CURSOR_CROSSHAIR: name = (LPCWSTR)IDC_CROSS; break;
684 case MOUSE_CURSOR_POINTING_HAND: name = (LPCWSTR)IDC_HAND; break;
685 case MOUSE_CURSOR_RESIZE_EW: name = (LPCWSTR)IDC_SIZEWE; break;
686 case MOUSE_CURSOR_RESIZE_NS: name = (LPCWSTR)IDC_SIZENS; break;
687 case MOUSE_CURSOR_RESIZE_NWSE: name = (LPCWSTR)IDC_SIZENWSE; break;
688 case MOUSE_CURSOR_RESIZE_NESW: name = (LPCWSTR)IDC_SIZENESW; break;
689 case MOUSE_CURSOR_RESIZE_ALL: name = (LPCWSTR)IDC_SIZEALL; break;
690 case MOUSE_CURSOR_NOT_ALLOWED: name = (LPCWSTR)IDC_NO; break;
691 default: break;
692 }
693
694 return name;
695}
696
697// Count monitors process
698// NOTE: Required by GetMonitorCount()
699static BOOL CALLBACK CountMonitorsProc(HMONITOR handle, HDC hdc, LPRECT rect, LPARAM lparam)
700{
701 int *count = (int *)lparam;
702 *count += 1;
703
704 // Always return TRUE to continue the loop, otherwise, the caller
705 // can't distinguish between stopping the loop and an error
706 return TRUE;
707}
708
709// Find monitor process
710// NOTE: Required by GetCurrentMonitor()
711static BOOL CALLBACK FindMonitorProc(HMONITOR handle, HDC hdc, LPRECT rect, LPARAM lparam)
712{
713 MonitorInfo *monitor = (MonitorInfo *)lparam;
714
715 if (handle == monitor->needle)
716 {
717 monitor->matchIndex = monitor->index;
718 monitor->rect = *rect;
719 }
720
721 monitor->index += 1;
722
723 // Always return TRUE to continue the loop, otherwise, the caller
724 // can't distinguish between stopping the loop and an error
725 return TRUE;
726}
727
728// Get style changed required operations flags
729// NOTE: Required for deferred operations
730static void GetStyleChangeFlagOps(DWORD coreWindowFlags, STYLESTRUCT *style, FlagsOp *deferredFlags)
731{
732 // Check window resizable flag change
733 bool resizable = (coreWindowFlags & FLAG_WINDOW_RESIZABLE);
734 bool resizableOld = ((style->styleOld & STYLE_FLAGS_RESIZABLE) != 0);
735 bool resizableNew = ((style->styleNew & STYLE_FLAGS_RESIZABLE) != 0);
736 if (resizable != resizableOld) TRACELOG(LOG_ERROR, "WIN32: Expected resizable %u but got %u", resizable, resizableOld);
737 if (resizableOld != resizableNew)
738 {
739 if (resizableNew) deferredFlags->set |= FLAG_WINDOW_RESIZABLE;
740 else deferredFlags->clear |= FLAG_WINDOW_RESIZABLE;
741 }
742
743 // Check window decorated flag change
744 bool decorated = (0 == (coreWindowFlags & FLAG_WINDOW_UNDECORATED));
745 bool decoratedOld = DecoratedFromStyle(style->styleOld);
746 bool decoratedNew = DecoratedFromStyle(style->styleNew);
747 if (decorated != decoratedOld) TRACELOG(LOG_ERROR, "WIN32: Expected decorated %u but got %u", decorated, decoratedOld);
748 if (decoratedOld != decoratedNew)
749 {
750 if (decoratedNew) deferredFlags->clear |= FLAG_WINDOW_UNDECORATED;
751 else deferredFlags->set |= FLAG_WINDOW_UNDECORATED;
752 }
753
754 // Check window hidden flag change
755 bool hidden = (coreWindowFlags & FLAG_WINDOW_HIDDEN);
756 bool hiddenOld = ((style->styleOld & WS_VISIBLE) == 0);
757 bool hiddenNew = ((style->styleNew & WS_VISIBLE) == 0);
758 if (hidden != hiddenOld) TRACELOG(LOG_ERROR, "WIN32: Expected hidden %u but got %u", hidden, hiddenOld);
759 if (hiddenOld != hiddenNew)
760 {
761 if (hiddenNew) deferredFlags->set |= FLAG_WINDOW_HIDDEN;
762 else deferredFlags->clear |= FLAG_WINDOW_HIDDEN;
763 }
764}
765
766// Adopt window resize
767// NOTE: Call when the window is rezised, returns true
768// if the new window size should update the desired app size
769static bool AdoptWindowResize(unsigned flags)
770{
771 if (flags & FLAG_WINDOW_MINIMIZED) return false;
772 if (flags & FLAG_WINDOW_MAXIMIZED) return false;
773 if (flags & FLAG_FULLSCREEN_MODE) return false;
774 if (flags & FLAG_BORDERLESS_WINDOWED_MODE) return false;
775 if (!(flags & FLAG_WINDOW_RESIZABLE)) return false;
776
777 return true;
778}
779
780// ---------------------------------------------------------------------------------------------
781// Here's the end of the "pure function section", the rest of the file can access global state
782// ---------------------------------------------------------------------------------------------
783
784// Unlock the ability to use CORE in the rest of the file
785#undef CORE
786
787//----------------------------------------------------------------------------------
788// Module Internal Functions Declaration
789//----------------------------------------------------------------------------------
790int InitPlatform(void); // Initialize platform (graphics, inputs and more)
791void ClosePlatform(void); // Close platform
792
793// Win32 process messages management function
794static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
795
796// Win32: Handle inputs functions
797static void HandleKey(WPARAM wparam, LPARAM lparam, char state);
798static void HandleMouseButton(int button, char state);
799static void HandleRawInput(LPARAM lparam);
800static void HandleWindowResize(HWND hwnd, int *width, int *height);
801
802static void UpdateWindowStyle(HWND hwnd, unsigned desiredFlags);
803static unsigned SanitizeFlags(int mode, unsigned flags);
804static void UpdateFlags(HWND hwnd, unsigned desiredFlags, int width, int height); // Update window flags
805
806// Check if OpenGL extension is available
807static bool IsWglExtensionAvailable(HDC hdc, const char *extension);
808
809//----------------------------------------------------------------------------------
810// Module Functions Declaration
811//----------------------------------------------------------------------------------
812// NOTE: Functions declaration is provided by raylib.h
813
814//----------------------------------------------------------------------------------
815// Module Functions Definition: Window and Graphics Device
816//----------------------------------------------------------------------------------
817
818// Check if application should close
819bool WindowShouldClose(void)
820{
821 return CORE.Window.shouldClose;
822}
823
824// Toggle fullscreen mode
825void ToggleFullscreen(void)
826{
827 TRACELOG(LOG_WARNING, "WIN32: Toggle full screen functionality not implemented");
828}
829
830// Toggle borderless windowed mode
831void ToggleBorderlessWindowed(void)
832{
833 if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) ClearWindowState(FLAG_BORDERLESS_WINDOWED_MODE);
834 else SetWindowState(FLAG_BORDERLESS_WINDOWED_MODE);
835}
836
837// Set window state: maximized, if resizable
838void MaximizeWindow(void)
839{
840 SetWindowState(FLAG_WINDOW_MAXIMIZED);
841}
842
843// Set window state: minimized
844void MinimizeWindow(void)
845{
846 SetWindowState(FLAG_WINDOW_MINIMIZED);
847}
848
849// Restore window from being minimized/maximized
850void RestoreWindow(void)
851{
852 if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) &&
853 (CORE.Window.flags & FLAG_WINDOW_MINIMIZED)) ClearWindowState(FLAG_WINDOW_MINIMIZED);
854 else ClearWindowState(FLAG_WINDOW_MINIMIZED | FLAG_WINDOW_MAXIMIZED);
855}
856
857// Set window configuration state using flags
858void SetWindowState(unsigned int flags)
859{
860 platform.desiredFlags = SanitizeFlags(1 /*SANITIZE_FLAGS_NORMAL*/, CORE.Window.flags | flags);
861 UpdateFlags(platform.hwnd, platform.desiredFlags, platform.appScreenWidth, platform.appScreenHeight);
862}
863
864// Clear window configuration state flags
865void ClearWindowState(unsigned int flags)
866{
867 platform.desiredFlags = SanitizeFlags(1 /*SANITIZE_FLAGS_NORMAL*/, CORE.Window.flags & ~flags);
868 UpdateFlags(platform.hwnd, platform.desiredFlags, platform.appScreenWidth, platform.appScreenHeight);
869}
870
871// Set icon for window
872void SetWindowIcon(Image image)
873{
874 if (!platform.hwnd || (image.data == NULL) || (image.width <= 0) || (image.height <= 0)) return;
875
876 HDC hdc = GetDC(platform.hwnd);
877
878 // Create 32-bit BGRA DIB for color
879 BITMAPV5HEADER bi = { 0 };
880 ZeroMemory(&bi, sizeof(bi));
881 bi.bV5Size = sizeof(bi);
882 bi.bV5Width = image.width;
883 bi.bV5Height = -image.height; // Negative = top-down bitmap
884 bi.bV5Planes = 1;
885 bi.bV5BitCount = 32;
886 bi.bV5Compression = BI_BITFIELDS;
887 bi.bV5RedMask = 0x00FF0000;
888 bi.bV5GreenMask = 0x0000FF00;
889 bi.bV5BlueMask = 0x000000FF;
890 bi.bV5AlphaMask = 0xFF000000;
891
892 unsigned char *targetBits = NULL;
893 HBITMAP hColorBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&targetBits, NULL, 0);
894 if (!hColorBitmap)
895 {
896 ReleaseDC(platform.hwnd, hdc);
897 return;
898 }
899
900 // Copy RGBA > BGRA (Win32 expects BGRA)
901 for (int y = 0; y < image.height; y++)
902 {
903 for (int x = 0; x < image.width; x++)
904 {
905 int i = (y*image.width + x)*4;
906 targetBits[i + 0] = ((unsigned char *)image.data)[i + 2]; // B
907 targetBits[i + 1] = ((unsigned char *)image.data)[i + 1]; // G
908 targetBits[i + 2] = ((unsigned char *)image.data)[i + 0]; // R
909 targetBits[i + 3] = ((unsigned char *)image.data)[i + 3]; // A
910 }
911 }
912
913 // Create mask bitmap (1-bit, all opaque)
914 HBITMAP hMaskBitmap = CreateBitmap(image.width, image.height, 1, 1, NULL);
915
916 // Build icon info
917 ICONINFO ii = { 0 };
918 ZeroMemory(&ii, sizeof(ii));
919 ii.fIcon = TRUE;
920 ii.hbmMask = hMaskBitmap;
921 ii.hbmColor = hColorBitmap;
922
923 HICON hIcon = CreateIconIndirect(&ii);
924
925 // Clean up GDI bitmaps (icon keeps copies internally)
926 DeleteObject(hColorBitmap);
927 DeleteObject(hMaskBitmap);
928 ReleaseDC(platform.hwnd, hdc);
929
930 if (hIcon)
931 {
932 // Set both large and small icons
933 SendMessage(platform.hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
934 SendMessage(platform.hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
935 }
936}
937
938// Set icon for window
939void SetWindowIcons(Image *images, int count)
940{
941 // TODO: Implement SetWindowIcons()
942}
943
944void SetWindowTitle(const char *title)
945{
946 CORE.Window.title = title;
947
948 WCHAR *titleWide = NULL;
949 A_TO_W_ALLOCA(titleWide, CORE.Window.title);
950
951 int result = SetWindowTextW(platform.hwnd, titleWide);
952 if (result == 0) TRACELOG(LOG_WARNING, "WIN32: Failed to set window title [ERROR: %lu]", GetLastError());
953}
954
955// Set window position on screen (windowed mode)
956void SetWindowPosition(int x, int y)
957{
958 if (platform.hwnd != NULL)
959 {
960 RECT rect = { 0 };
961 if (GetWindowRect(platform.hwnd, &rect))
962 {
963 int width = rect.right - rect.left;
964 int height = rect.bottom - rect.top;
965
966 // Move the window to the new position (keeping size and z-order)
967 SetWindowPos(platform.hwnd, NULL, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
968 }
969 }
970}
971
972// Set monitor for the current window
973void SetWindowMonitor(int monitor)
974{
975 TRACELOG(LOG_WARNING, "SetWindowMonitor not implemented");
976}
977
978// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
979void SetWindowMinSize(int width, int height)
980{
981 TRACELOG(LOG_WARNING, "SetWindowMinSize not implemented");
982
983 CORE.Window.screenMin.width = width;
984 CORE.Window.screenMin.height = height;
985}
986
987// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
988void SetWindowMaxSize(int width, int height)
989{
990 TRACELOG(LOG_WARNING, "SetWindowMaxSize not implemented");
991
992 CORE.Window.screenMax.width = width;
993 CORE.Window.screenMax.height = height;
994}
995
996// Set window dimensions
997void SetWindowSize(int width, int height)
998{
999 TRACELOG(LOG_WARNING, "SetWindowSize not implemented");
1000}
1001
1002// Set window opacity, value opacity is between 0.0 and 1.0
1003void SetWindowOpacity(float opacity)
1004{
1005 TRACELOG(LOG_WARNING, "SetWindowOpacity not implemented");
1006}
1007
1008// Set window focused
1009void SetWindowFocused(void)
1010{
1011 TRACELOG(LOG_WARNING, "SetWindowFocused not implemented");
1012}
1013
1014// Get native window handle
1015void *GetWindowHandle(void)
1016{
1017 return platform.hwnd;
1018}
1019
1020int GetMonitorCount(void)
1021{
1022 int count = 0;
1023
1024 int result = EnumDisplayMonitors(NULL, NULL, CountMonitorsProc, (LPARAM)&count);
1025 if (result == 0) TRACELOG(LOG_ERROR, "%s failed, error=%lu", "EnumDisplayMonitors", GetLastError());
1026
1027 return count;
1028}
1029
1030// Get current monitor where window is placed
1031int GetCurrentMonitor(void)
1032{
1033 HMONITOR monitor = MonitorFromWindow(platform.hwnd, MONITOR_DEFAULTTOPRIMARY);
1034 if (!monitor) TRACELOG(LOG_ERROR, "%s failed, error=%lu", "MonitorFromWindow", GetLastError());
1035
1036 MonitorInfo info = { 0 };
1037 info.needle = monitor;
1038 info.index = 0;
1039 info.matchIndex = -1;
1040
1041 int result = EnumDisplayMonitors(NULL, NULL, FindMonitorProc, (LPARAM)&info);
1042 if (result == 0) TRACELOG(LOG_ERROR, "%s failed, error=%lu", "EnumDisplayMonitors", GetLastError());
1043
1044 return info.matchIndex;
1045}
1046
1047// Get selected monitor position
1048Vector2 GetMonitorPosition(int monitor)
1049{
1050 TRACELOG(LOG_WARNING, "GetMonitorPosition not implemented");
1051 return (Vector2){ 0, 0 };
1052}
1053
1054// Get selected monitor width (currently used by monitor)
1055int GetMonitorWidth(int monitor)
1056{
1057 TRACELOG(LOG_WARNING, "GetMonitorWidth not implemented");
1058 return 0;
1059}
1060
1061// Get selected monitor height (currently used by monitor)
1062int GetMonitorHeight(int monitor)
1063{
1064 TRACELOG(LOG_WARNING, "GetMonitorHeight not implemented");
1065 return 0;
1066}
1067
1068// Get selected monitor physical width in millimetres
1069int GetMonitorPhysicalWidth(int monitor)
1070{
1071 TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth not implemented");
1072 return 0;
1073}
1074
1075// Get selected monitor physical height in millimetres
1076int GetMonitorPhysicalHeight(int monitor)
1077{
1078 TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight not implemented");
1079 return 0;
1080}
1081
1082// Get selected monitor refresh rate
1083int GetMonitorRefreshRate(int monitor)
1084{
1085 TRACELOG(LOG_WARNING, "GetMonitorRefreshRate not implemented");
1086 return 0;
1087}
1088
1089// Get the human-readable, UTF-8 encoded name of the selected monitor
1090const char *GetMonitorName(int monitor)
1091{
1092 TRACELOG(LOG_WARNING, "GetMonitorName not implemented");
1093 return 0;
1094}
1095
1096// Get window position XY on monitor
1097Vector2 GetWindowPosition(void)
1098{
1099 TRACELOG(LOG_WARNING, "GetWindowPosition not implemented");
1100 return (Vector2){ 0, 0 };
1101}
1102
1103// Get window scale DPI factor for current monitor
1104Vector2 GetWindowScaleDPI(void)
1105{
1106 float scale = ((float)GetDpiForWindow(platform.hwnd))/96.0f;
1107 return (Vector2){ scale, scale };
1108}
1109
1110// Set clipboard text content
1111void SetClipboardText(const char *text)
1112{
1113 TRACELOG(LOG_WARNING, "SetClipboardText not implemented");
1114}
1115
1116// Get clipboard text content
1117const char *GetClipboardText(void)
1118{
1119 TRACELOG(LOG_WARNING, "GetClipboardText not implemented");
1120 return NULL;
1121}
1122
1123// Get clipboard image
1124Image GetClipboardImage(void)
1125{
1126 Image image = { 0 };
1127
1128 TRACELOG(LOG_WARNING, "GetClipboardText not implemented");
1129
1130 return image;
1131}
1132
1133// Show mouse cursor
1134void ShowCursor(void)
1135{
1136 SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_ARROW));
1137 CORE.Input.Mouse.cursorHidden = false;
1138}
1139
1140// Hides mouse cursor
1141void HideCursor(void)
1142{
1143 // NOTE: We use SetCursor() instead of ShowCursor() because
1144 // it makes it easy to only hide the cursor while it's inside the client area
1145 SetCursor(NULL);
1146 CORE.Input.Mouse.cursorHidden = true;
1147}
1148
1149// Enables cursor (unlock cursor)
1150void EnableCursor(void)
1151{
1152 if (CORE.Input.Mouse.cursorLocked)
1153 {
1154 if (!ClipCursor(NULL)) TRACELOG(LOG_WARNING, "WIN32: Failed to clip cursor [ERROR: %lu]", GetLastError());
1155
1156 RAWINPUTDEVICE rid = { 0 };
1157 rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
1158 rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
1159 rid.dwFlags = RIDEV_REMOVE; // Add to this window even in background
1160 rid.hwndTarget = NULL;
1161 int result = RegisterRawInputDevices(&rid, 1, sizeof(rid));
1162 if (result == 0) TRACELOG(LOG_WARNING, "WIN32: Failed to register raw input devices [ERROR: %lu]", GetLastError());
1163
1164 ShowCursor();
1165 CORE.Input.Mouse.cursorLocked = false;
1166 }
1167}
1168
1169// Disables cursor (lock cursor)
1170void DisableCursor(void)
1171{
1172 if (!CORE.Input.Mouse.cursorLocked)
1173 {
1174 RAWINPUTDEVICE rid = { 0 };
1175 rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
1176 rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
1177 rid.dwFlags = RIDEV_INPUTSINK; // Add to this window even in background
1178 rid.hwndTarget = platform.hwnd;
1179 int result = RegisterRawInputDevices(&rid, 1, sizeof(rid));
1180 if (result == 0) TRACELOG(LOG_WARNING, "WIN32: Failed to register raw input devices [ERROR: %lu]", GetLastError());
1181
1182 RECT clientRect = { 0 };
1183 if (!GetClientRect(platform.hwnd, &clientRect)) TRACELOG(LOG_WARNING, "WIN32: Failed to get client rectangle [ERROR: %lu]", GetLastError());
1184
1185 POINT topleft = { clientRect.left, clientRect.top };
1186 if (!ClientToScreen(platform.hwnd, &topleft)) TRACELOG(LOG_WARNING, "WIN32: Failed to get client to screen size [ERROR: %lu]", GetLastError());
1187
1188 LONG width = clientRect.right - clientRect.left;
1189 LONG height = clientRect.bottom - clientRect.top;
1190
1191 TRACELOG(LOG_INFO, "WIN32: Clip cursor client rect: [%d,%d %d,%d], top-left: (%d,%d)",
1192 clientRect.left, clientRect.top, clientRect.right, clientRect.bottom, topleft.x, topleft.y);
1193
1194 LONG centerX = topleft.x + width/2;
1195 LONG centerY = topleft.y + height/2;
1196 RECT clipRect = { centerX, centerY, centerX + 1, centerY + 1 };
1197 if (!ClipCursor(&clipRect)) TRACELOG(LOG_WARNING, "WIN32: Failed to clip cursor [ERROR: %lu]", GetLastError());
1198
1199 CORE.Input.Mouse.previousPosition = (Vector2){ 0, 0 };
1200 CORE.Input.Mouse.currentPosition = (Vector2){ 0, 0 };
1201 HideCursor();
1202
1203 CORE.Input.Mouse.cursorLocked = true;
1204 }
1205}
1206
1207// Swap back buffer with front buffer (screen drawing)
1208void SwapScreenBuffer(void)
1209{
1210 if (!platform.hdc) abort();
1211
1212#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
1213 // Update framebuffer
1214 rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels);
1215
1216 // Force redraw
1217 InvalidateRect(platform.hwnd, NULL, FALSE);
1218 UpdateWindow(platform.hwnd);
1219#else
1220 if (!SwapBuffers(platform.hdc)) TRACELOG(LOG_ERROR, "WIN32: Failed to swap buffers [ERROR: %lu]", GetLastError());
1221 if (!ValidateRect(platform.hwnd, NULL)) TRACELOG(LOG_ERROR, "WIN32: Failed to validate screen rect [ERROR: %lu]", GetLastError());
1222#endif
1223}
1224
1225//----------------------------------------------------------------------------------
1226// Module Functions Definition: Misc
1227//----------------------------------------------------------------------------------
1228
1229// Get elapsed time measure in seconds
1230double GetTime(void)
1231{
1232 LARGE_INTEGER now = 0;
1233 QueryPerformanceCounter(&now);
1234 return (double)(now.QuadPart - CORE.Time.base)/(double)platform.timerFrequency.QuadPart;
1235}
1236
1237// Open URL with default system browser (if available)
1238// NOTE: This function is only safe to use if you control the URL given
1239// A user could craft a malicious string performing another action
1240// Only call this function yourself not with user input or make sure to check the string yourself
1241// REF: https://github.com/raysan5/raylib/issues/686
1242void OpenURL(const char *url)
1243{
1244 // Security check to (partially) avoid malicious code on target platform
1245 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
1246 else
1247 {
1248 char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
1249 sprintf(cmd, "explorer \"%s\"", url);
1250 int result = system(cmd);
1251 if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
1252 RL_FREE(cmd);
1253 }
1254}
1255
1256//----------------------------------------------------------------------------------
1257// Module Functions Definition: Inputs
1258//----------------------------------------------------------------------------------
1259
1260// Set internal gamepad mappings
1261int SetGamepadMappings(const char *mappings)
1262{
1263 TRACELOG(LOG_WARNING, "SetGamepadMappings not implemented");
1264
1265 return -1;
1266}
1267
1268// Set gamepad vibration
1269void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
1270{
1271 TRACELOG(LOG_WARNING, "SetGamepadVibration not implemented");
1272}
1273
1274// Set mouse position XY
1275void SetMousePosition(int x, int y)
1276{
1277 if (!CORE.Input.Mouse.cursorLocked)
1278 {
1279 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
1280 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
1281 TRACELOG(LOG_WARNING, "SetMousePosition not implemented");
1282 }
1283 else TRACELOG(LOG_WARNING, "INPUT: MOUSE: Cursor not enabled");
1284}
1285
1286// Set mouse cursor
1287void SetMouseCursor(int cursor)
1288{
1289 LPCWSTR cursorName = GetCursorName(cursor);
1290 HCURSOR hcursor = LoadCursorW(NULL, cursorName);
1291 if (!hcursor) TRACELOG(LOG_ERROR, "WIN32: Failed to load requested cursor [ERROR: %lu]", GetLastError());
1292
1293 SetCursor(hcursor);
1294 CORE.Input.Mouse.cursorHidden = false;
1295}
1296
1297// Get physical key name
1298const char *GetKeyName(int key)
1299{
1300 TRACELOG(LOG_WARNING, "GetKeyName not implemented");
1301 return NULL;
1302}
1303
1304// Register all input events
1305void PollInputEvents(void)
1306{
1307 // Reset keys/chars pressed registered
1308 CORE.Input.Keyboard.keyPressedQueueCount = 0;
1309 CORE.Input.Keyboard.charPressedQueueCount = 0;
1310
1311 // Reset key repeats
1312 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
1313
1314 // Reset last gamepad button/axis registered state
1315 CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
1316 //CORE.Input.Gamepad.axisCount = 0;
1317
1318 // Register previous touch states
1319 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
1320
1321 // Reset touch positions
1322 // TODO: It resets on target platform the mouse position and not filled again until a move-event,
1323 // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
1324 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
1325
1326 memcpy(CORE.Input.Keyboard.previousKeyState, CORE.Input.Keyboard.currentKeyState, sizeof(CORE.Input.Keyboard.previousKeyState));
1327 memset(CORE.Input.Keyboard.keyRepeatInFrame, 0, sizeof(CORE.Input.Keyboard.keyRepeatInFrame));
1328
1329 // Register previous mouse wheel state
1330 CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
1331 CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
1332
1333 // Register previous mouse position
1334 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
1335
1336 // Process windows messages
1337 MSG msg = { 0 };
1338 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
1339 {
1340 TranslateMessage(&msg);
1341 DispatchMessageW(&msg);
1342 }
1343}
1344
1345//----------------------------------------------------------------------------------
1346// Module Internal Functions Definition
1347//----------------------------------------------------------------------------------
1348
1349// Initialize modern OpenGL context
1350// NOTE: We need to create a dummy context first to query required extensions
1351HGLRC InitOpenGL(HWND hwnd, HDC hdc)
1352{
1353 // First, create a dummy context to get WGL extensions
1354 PIXELFORMATDESCRIPTOR pixelFormatDesc = {
1355 .nSize = sizeof(PIXELFORMATDESCRIPTOR),
1356 .nVersion = 1,
1357 .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
1358 .iPixelType = PFD_TYPE_RGBA,
1359 .cColorBits = 32,
1360 .cAlphaBits = 8,
1361 .cDepthBits = 24,
1362 .iLayerType = PFD_MAIN_PLANE
1363 };
1364
1365 int pixelFormat = ChoosePixelFormat(hdc, &pixelFormatDesc);
1366 SetPixelFormat(hdc, pixelFormat, &pixelFormatDesc);
1367 //int pixelFormat = ChoosePixelFormat(platform.hdc, &pixelFormatDesc);
1368 //if (!pixelFormat) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "ChoosePixelFormat", GetLastError()); return -1; }
1369 //if (!SetPixelFormat(platform.hdc, pixelFormat, &pixelFormatDesc)) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "SetPixelFormat", GetLastError()); return -1; }
1370
1371 HGLRC tempContext = wglCreateContext(hdc);
1372 //if (!tempContext) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "wglCreateContext", GetLastError()); return -1; }
1373 BOOL result = wglMakeCurrent(hdc, tempContext);
1374 //if (!result) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "wglMakeCurrent", GetLastError()); return -1; }
1375
1376 // Load WGL extension entry points
1377 wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
1378 wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
1379 wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
1380 wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
1381
1382 // Setup modern pixel format if extension is available
1383 if (wglChoosePixelFormatARB)
1384 {
1385 int pixelFormatAttribs[] = {
1386 WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
1387 WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
1388 WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
1389 WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
1390 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
1391 WGL_COLOR_BITS_ARB, 32,
1392 //WGL_RED_BITS_ARB, 8,
1393 //WGL_GREEN_BITS_ARB, 8,
1394 //WGL_BLUE_BITS_ARB, 8,
1395 //WGL_ALPHA_BITS_ARB, 8,
1396 WGL_DEPTH_BITS_ARB, 24,
1397 WGL_STENCIL_BITS_ARB, 8,
1398 0 // Terminator
1399 };
1400
1401 int format = 0;
1402 UINT numFormats = 0;
1403 if (wglChoosePixelFormatARB(hdc, pixelFormatAttribs, NULL, 1, &format, &numFormats) && (numFormats > 0))
1404 {
1405 PIXELFORMATDESCRIPTOR newPixelFormatDescriptor = { 0 };
1406 DescribePixelFormat(hdc, format, sizeof(newPixelFormatDescriptor), &newPixelFormatDescriptor);
1407 SetPixelFormat(hdc, format, &newPixelFormatDescriptor);
1408 }
1409 }
1410
1411 // Create real modern OpenGL context (3.3 core)
1412 HGLRC realContext = NULL;
1413 if (wglCreateContextAttribsARB)
1414 {
1415 int glContextVersionMajor = 1;
1416 int glContextVersionMinor = 1;
1417 int glContextProfile = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
1418
1419 if (rlGetVersion() == RL_OPENGL_21) // Request OpenGL 2.1 context
1420 {
1421 glContextVersionMajor = 2;
1422 glContextVersionMinor = 1;
1423 }
1424 else if (rlGetVersion() == RL_OPENGL_33) // Request OpenGL 3.3 context
1425 {
1426 glContextVersionMajor = 3;
1427 glContextVersionMinor = 3;
1428 }
1429 else if (rlGetVersion() == RL_OPENGL_43) // Request OpenGL 4.3 context
1430 {
1431 glContextVersionMajor = 4;
1432 glContextVersionMinor = 3;
1433 }
1434 else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context
1435 {
1436 if (IsWglExtensionAvailable(platform.hdc, "WGL_EXT_create_context_es_profile") ||
1437 IsWglExtensionAvailable(platform.hdc, "WGL_EXT_create_context_es2_profile"))
1438 {
1439 glContextVersionMajor = 2;
1440 glContextVersionMinor = 0;
1441 glContextProfile = WGL_CONTEXT_ES_PROFILE_BIT_EXT;
1442 }
1443 else TRACELOG(LOG_WARNING, "GL: OpenGL ES context not supported by GPU");
1444 }
1445 else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context
1446 {
1447 if (IsWglExtensionAvailable(platform.hdc, "WGL_EXT_create_context_es_profile") ||
1448 IsWglExtensionAvailable(platform.hdc, "WGL_EXT_create_context_es2_profile"))
1449 {
1450 glContextVersionMajor = 3;
1451 glContextVersionMinor = 0;
1452 glContextProfile = WGL_CONTEXT_ES_PROFILE_BIT_EXT;
1453 }
1454 else TRACELOG(LOG_WARNING, "GL: OpenGL ES context not supported by GPU");
1455 }
1456
1457 int contextAttribs[] = {
1458 WGL_CONTEXT_MAJOR_VERSION_ARB, glContextVersionMajor,
1459 WGL_CONTEXT_MINOR_VERSION_ARB, glContextVersionMinor,
1460 WGL_CONTEXT_PROFILE_MASK_ARB, glContextProfile, // WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, WGL_CONTEXT_ES_PROFILE_BIT_EXT (if supported)
1461 //WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB [glDebugMessageCallback()]
1462 0 // Terminator
1463 };
1464
1465 // NOTE: We are not sharing context resources so, second parameters is NULL
1466 realContext = wglCreateContextAttribsARB(hdc, NULL, contextAttribs);
1467
1468 // Check for error context creation errors
1469 // ERROR_INVALID_VERSION_ARB (0x2095)
1470 // ERROR_INVALID_PROFILE_ARB (0x2096)
1471 if (realContext == NULL) TRACELOG(LOG_ERROR, "GL: Error creating requested context: %lu", GetLastError());
1472 }
1473
1474 // Cleanup dummy temp context
1475 wglMakeCurrent(NULL, NULL);
1476 wglDeleteContext(tempContext);
1477
1478 // Activate real context
1479 if (realContext) wglMakeCurrent(hdc, realContext);
1480
1481 // Once we got a real modern OpenGL context,
1482 // we can load required extensions (function pointers)
1483 rlLoadExtensions(WglGetProcAddress);
1484
1485 return realContext;
1486}
1487
1488// Initialize platform: graphics, inputs and more
1489int InitPlatform(void)
1490{
1491 int result = 0;
1492
1493 platform.appScreenWidth = CORE.Window.screen.width;
1494 platform.appScreenHeight = CORE.Window.screen.height;
1495 platform.desiredFlags = SanitizeFlags(0 /*SANITIZE_FLAGS_FIRST*/, CORE.Window.flags);
1496
1497 // NOTE: From this point CORE.Window.flags should always reflect the actual state of the window
1498 CORE.Window.flags = FLAG_WINDOW_HIDDEN | (platform.desiredFlags & FLAG_MASK_NO_UPDATE);
1499
1500/*
1501 // TODO: Review SetProcessDpiAwarenessContext()
1502 // NOTE: SetProcessDpiAwarenessContext() requires Windows 10, version 1703 and shcore.lib linkage
1503 if (IsWindows10Version1703OrGreaterWin32())
1504 {
1505 TRACELOG(LOG_INFO, "DpiAware: >=Win10Creators");
1506 if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
1507 TRACELOG(LOG_ERROR, "%s failed, error %u", "SetProcessDpiAwarenessContext", GetLastError());
1508 }
1509 else
1510 {
1511 TRACELOG(LOG_INFO, "DpiAware: <Win10Creators");
1512 HRESULT hr = SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
1513 if (hr < 0) TRACELOG(LOG_ERROR, "%s failed, hresult=0x%lx", "SetProcessDpiAwareness", (DWORD)hr);
1514 }
1515*/
1516
1517 HINSTANCE hInstance = GetModuleHandleW(0);
1518
1519 // Define window class
1520 WNDCLASSEXW windowClass = {
1521 .cbSize = sizeof(WNDCLASSEXW),
1522 .style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
1523 .lpfnWndProc = WndProc, // Custom procedure assigned
1524 .cbWndExtra = sizeof(LONG_PTR), // extra space for the Tuple object ptr
1525 .hInstance = hInstance,
1526 .hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW), // TODO: Audit if we want to set this since we're implementing WM_SETCURSOR
1527 .lpszClassName = CLASS_NAME // Class name: L"raylibWindow"
1528 };
1529
1530 // Load user-provided icon if available
1531 // NOTE: raylib resource file defaults to GLFW_ICON id, so looking for same identifier
1532 windowClass.hIcon = LoadImageW(hInstance, L"GLFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
1533 if (!windowClass.hIcon) windowClass.hIcon = LoadImageW(NULL, (LPCWSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
1534
1535 // Register window class
1536 result = (int)RegisterClassExW(&windowClass);
1537 if (result == 0) TRACELOG(LOG_ERROR, "WIN32: Failed to register window class [ERROR: %lu]", GetLastError());
1538
1539 // Get primary monitor info
1540 POINT primaryTopLeft = { 0 };
1541 HMONITOR monitor = MonitorFromPoint(primaryTopLeft, MONITOR_DEFAULTTOPRIMARY);
1542 if (monitor != NULL)
1543 {
1544 MONITORINFO info = { 0 };
1545 info.cbSize = sizeof(info);
1546 result = (int)GetMonitorInfoW(monitor, &info);
1547
1548 if (result == 0) TRACELOG(LOG_WARNING, "WIN32: DISPLAY: Failed to get monitor info [ERROR: %u]", GetLastError());
1549 else
1550 {
1551 CORE.Window.display.width = info.rcMonitor.right - info.rcMonitor.left;
1552 CORE.Window.display.height = info.rcMonitor.bottom - info.rcMonitor.top;
1553 }
1554 }
1555 else TRACELOG(LOG_WARNING, "WIN32: DISPLAY: Failed to get primary monitor from point [ERROR: %u]", GetLastError());
1556
1557 // Adjust the window rectangle so the *client area* matches desired size
1558 // NOTE: Window width/height includes borders and title-bar
1559 DWORD style = WS_OVERLAPPEDWINDOW;
1560 RECT rect = { 0, 0, platform.appScreenWidth, platform.appScreenHeight };
1561 AdjustWindowRect(&rect, style, FALSE);
1562 //AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, FALSE, WINDOW_STYLE_EX);
1563 //AdjustWindowRectExForDpi(&rect, style, FALSE, WINDOW_STYLE_EX, dpi);
1564 int windowWidth = rect.right - rect.left;
1565 int windowHeight = rect.bottom - rect.top;
1566
1567 // Create window
1568 // NOTE: Title string needs to be converted to WCHAR
1569 WCHAR *titleWide = NULL;
1570 A_TO_W_ALLOCA(titleWide, CORE.Window.title);
1571
1572 // Create window and get handle
1573 platform.hwnd = CreateWindowExW(
1574 WINDOW_STYLE_EX,
1575 CLASS_NAME,
1576 titleWide,
1577 MakeWindowStyle(CORE.Window.flags), // WS_OVERLAPPEDWINDOW | WS_VISIBLE
1578 CW_USEDEFAULT, CW_USEDEFAULT,
1579 windowWidth, windowHeight, // TODO: Window size [width, height], needs to be updated?
1580 NULL, NULL,
1581 GetModuleHandleW(NULL), NULL);
1582
1583 if (!platform.hwnd)
1584 {
1585 TRACELOG(LOG_ERROR, "WIN32: WINDOW: Failed to create window [ERROR: %lu]", GetLastError());
1586 return -1;
1587 }
1588
1589 // Get handle to device drawing context
1590 // NOTE: Windows GDI object that represents a drawing surface
1591 platform.hdc = GetDC(platform.hwnd);
1592
1593 if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer
1594 {
1595 //ShowWindow(platform.hwnd, SW_SHOWDEFAULT); //SW_SHOWNORMAL
1596
1597 // Initialize software framebuffer
1598 BITMAPINFO bmi = { 0 };
1599 ZeroMemory(&bmi, sizeof(bmi));
1600 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
1601 bmi.bmiHeader.biWidth = platform.appScreenWidth;
1602 bmi.bmiHeader.biHeight = -(int)(platform.appScreenHeight); // Top-down bitmap
1603 bmi.bmiHeader.biPlanes = 1;
1604 bmi.bmiHeader.biBitCount = 32; // 32-bit BGRA
1605 bmi.bmiHeader.biCompression = BI_RGB;
1606
1607 platform.hdcmem = CreateCompatibleDC(platform.hdc);
1608
1609 platform.hbitmap = CreateDIBSection(
1610 platform.hdcmem, &bmi, DIB_RGB_COLORS,
1611 (void **)&platform.pixels, NULL, 0);
1612
1613 SelectObject(platform.hdcmem, platform.hbitmap);
1614
1615 //ReleaseDC(platform.hwnd, platform.hdc); // Required?
1616 }
1617 else
1618 {
1619 // Init hardware-accelerated OpenGL modern context
1620 platform.glContext = InitOpenGL(platform.hwnd, platform.hdc);
1621 }
1622
1623 CORE.Window.ready = true;
1624
1625 // Update flags (in case of deferred state change required)
1626 UpdateFlags(platform.hwnd, platform.desiredFlags, platform.appScreenWidth, platform.appScreenHeight);
1627
1628 CORE.Window.render.width = CORE.Window.screen.width;
1629 CORE.Window.render.height = CORE.Window.screen.height;
1630 CORE.Window.currentFbo.width = CORE.Window.render.width;
1631 CORE.Window.currentFbo.height = CORE.Window.render.height;
1632 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
1633 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1634 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
1635 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
1636 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
1637
1638 if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer
1639 {
1640 TRACELOG(LOG_INFO, "GL: OpenGL device information:");
1641 TRACELOG(LOG_INFO, " > Vendor: %s", "raylib");
1642 TRACELOG(LOG_INFO, " > Renderer: %s", "rlsw - OpenGL 1.1 Software Renderer");
1643 TRACELOG(LOG_INFO, " > Version: %s", "1.0");
1644 TRACELOG(LOG_INFO, " > GLSL: %s", "NOT SUPPORTED");
1645 }
1646
1647 // Initialize timming system
1648 //----------------------------------------------------------------------------
1649 LARGE_INTEGER time = { 0 };
1650 QueryPerformanceCounter(&time);
1651 QueryPerformanceFrequency(&platform.timerFrequency);
1652 CORE.Time.base = time.QuadPart;
1653
1654 InitTimer();
1655 //----------------------------------------------------------------------------
1656
1657 // Initialize storage system
1658 //----------------------------------------------------------------------------
1659 CORE.Storage.basePath = GetWorkingDirectory();
1660 //----------------------------------------------------------------------------
1661
1662 TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: WIN32: Initialized successfully");
1663
1664 return 0;
1665}
1666
1667// Close platform
1668void ClosePlatform(void)
1669{
1670 if (platform.hwnd)
1671 {
1672 int result = DestroyWindow(platform.hwnd);
1673 if (result == 0) TRACELOG(LOG_WARNING, "WIN32: WINDOW: Failed on window destroy [ERROR: %u]", GetLastError());
1674 platform.hwnd = NULL;
1675 }
1676}
1677
1678// Window procedure, message processing callback
1679// NOTE: All window event messages are processed here
1680static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
1681{
1682 LRESULT result = 0;
1683
1684 // Sanity check
1685 DWORD mask = STYLE_MASK_ALL;
1686 if (platform.hwnd == hwnd)
1687 {
1688 if (msg == WM_WINDOWPOSCHANGING) mask &= ~(WS_MINIMIZE | WS_MAXIMIZE);
1689 CheckFlags("WndProc", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), mask);
1690 }
1691
1692 FlagsOp flagsOp = { 0 };
1693 FlagsOp *deferredFlags = &flagsOp;
1694
1695 // Message processing
1696 //------------------------------------------------------------------------------------
1697 switch (msg)
1698 {
1699 case WM_CREATE:
1700 {
1701 // WARNING: Not recommended to do OpenGL intialization at this point
1702
1703 } break;
1704 //case WM_ACTIVATE
1705 case WM_DESTROY:
1706 {
1707 // Clean up for window destruction
1708 if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer
1709 {
1710 if (platform.hdcmem)
1711 {
1712 DeleteDC(platform.hdcmem);
1713 platform.hdcmem = NULL;
1714 }
1715
1716 if (platform.hbitmap)
1717 {
1718 DeleteObject(platform.hbitmap); // Clears platform.pixels data
1719 platform.hbitmap = NULL;
1720 platform.pixels = NULL; // NOTE: Pointer invalid after DeleteObject()
1721 }
1722 }
1723 else // OpenGL hardware renderer
1724 {
1725 wglMakeCurrent(platform.hdc, NULL);
1726 if (platform.glContext)
1727 {
1728 if (!wglDeleteContext(platform.glContext)) abort();
1729 platform.glContext = NULL;
1730 }
1731 }
1732
1733 if (platform.hdc)
1734 {
1735 if (!ReleaseDC(hwnd, platform.hdc)) abort();
1736 platform.hdc = NULL;
1737 }
1738
1739 PostQuitMessage(0);
1740
1741 } break;
1742 case WM_CLOSE: CORE.Window.shouldClose = true; break; // Window close button [x], ALT+F4
1743 //case WM_QUIT: // Application closing, not related to window
1744 case WM_KILLFOCUS:
1745 {
1746 memset(CORE.Input.Keyboard.previousKeyState, 0, sizeof(CORE.Input.Keyboard.previousKeyState));
1747 memset(CORE.Input.Keyboard.currentKeyState, 0, sizeof(CORE.Input.Keyboard.currentKeyState));
1748 } break;
1749 case WM_SIZING:
1750 {
1751 if (CORE.Window.flags & FLAG_WINDOW_RESIZABLE)
1752 {
1753 // TODO: Enforce min/max size
1754 }
1755 else TRACELOG(LOG_WARNING, "WIN32: WINDOW: Trying to resize a non-resizable window");
1756
1757 result = TRUE;
1758 } break;
1759 case WM_STYLECHANGING:
1760 {
1761 if (wparam == GWL_STYLE)
1762 {
1763 STYLESTRUCT *ss = (STYLESTRUCT *)lparam;
1764 GetStyleChangeFlagOps(CORE.Window.flags, ss, deferredFlags);
1765
1766 UINT dpi = GetDpiForWindow(hwnd);
1767 // Get client size (framebuffer inside the window)
1768 RECT rect = { 0 };
1769 GetClientRect(hwnd, &rect);
1770 SIZE clientSize = { rect.right, rect.bottom };
1771 SIZE oldSize = CalcWindowSize(dpi, clientSize, ss->styleOld);
1772 SIZE newSize = CalcWindowSize(dpi, clientSize, ss->styleNew);
1773
1774 if (oldSize.cx != newSize.cx || oldSize.cy != newSize.cy)
1775 {
1776 TRACELOG(LOG_INFO, "WIN32: WINDOW: Resize from style change [%dx%d] to [%dx%d]", oldSize.cx, oldSize.cy, newSize.cx, newSize.cy);
1777
1778 if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED)
1779 {
1780 // looks like windows will automatically "unminimize" a window
1781 // if a style changes modifies it's size
1782 TRACELOG(LOG_INFO, "WIN32: WINDOW: Style change modifed window size, removing maximized flag");
1783 deferredFlags->clear |= FLAG_WINDOW_MAXIMIZED;
1784 }
1785 }
1786 }
1787 } break;
1788 case WM_WINDOWPOSCHANGING:
1789 {
1790 WINDOWPOS *pos = (WINDOWPOS *)lparam;
1791 if (pos->flags & SWP_SHOWWINDOW) deferredFlags->clear |= FLAG_WINDOW_HIDDEN;
1792 else if (pos->flags & SWP_HIDEWINDOW) deferredFlags->set |= FLAG_WINDOW_HIDDEN;
1793
1794 Mized mized = MIZED_NONE;
1795 bool isIconic = IsIconic(hwnd);
1796 bool styleMinimized = !!(WS_MINIMIZE & GetWindowLongPtrW(hwnd, GWL_STYLE));
1797 if (isIconic != styleMinimized) TRACELOG(LOG_WARNING, "WIN32: IsIconic state different from WS_MINIMIZED state");
1798
1799 if (isIconic) mized = MIZED_MIN;
1800 else
1801 {
1802 WINDOWPLACEMENT placement;
1803 placement.length = sizeof(placement);
1804 if (!GetWindowPlacement(hwnd, &placement)) TRACELOG(LOG_ERROR, "WIN32: WINDOW: FAiled to get monitor placement [ERROR: %lu]", GetLastError());
1805
1806 if (placement.showCmd == SW_SHOWMAXIMIZED) mized = MIZED_MAX;
1807 }
1808
1809 switch (mized)
1810 {
1811 case MIZED_NONE:
1812 {
1813 deferredFlags->clear |= (FLAG_WINDOW_MINIMIZED | FLAG_WINDOW_MAXIMIZED);
1814 HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
1815 MONITORINFO info;
1816 info.cbSize = sizeof(info);
1817 if (!GetMonitorInfoW(monitor, &info)) TRACELOG(LOG_ERROR, "WIN32: MONITOR: Failed to get monitor info [ERROR: %lu]", GetLastError());
1818
1819 if ((pos->x == info.rcMonitor.left) &&
1820 (pos->y == info.rcMonitor.top) &&
1821 (pos->cx == (info.rcMonitor.right - info.rcMonitor.left)) &&
1822 (pos->cy == (info.rcMonitor.bottom - info.rcMonitor.top))) deferredFlags->set |= FLAG_BORDERLESS_WINDOWED_MODE;
1823 else deferredFlags->clear |= FLAG_BORDERLESS_WINDOWED_MODE;
1824
1825 } break;
1826 case MIZED_MIN:
1827 {
1828 // !!! NOTE !!! Do not update the maximized/borderless
1829 // flags because when hwnd is minimized it temporarily overrides
1830 // the maximized state/flag which gets restored on SW_RESTORE
1831 deferredFlags->set |= FLAG_WINDOW_MINIMIZED;
1832 } break;
1833 case MIZED_MAX:
1834 {
1835 deferredFlags->clear |= FLAG_WINDOW_MINIMIZED;
1836 deferredFlags->set |= FLAG_WINDOW_MAXIMIZED;
1837 } break;
1838 default: break;
1839 }
1840 } break;
1841 case WM_SIZE:
1842 {
1843 // WARNING: Don't trust the docs, they say you won't get this message if you don't call DefWindowProc
1844 // in response to WM_WINDOWPOSCHANGED but looks like when a window is created you'll get this
1845 // message without getting WM_WINDOWPOSCHANGED
1846 HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight);
1847 } break;
1848 //case WM_MOVE
1849 case WM_WINDOWPOSCHANGED:
1850 {
1851 WINDOWPOS *pos = (WINDOWPOS*)lparam;
1852 if (!(pos->flags & SWP_NOSIZE)) HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight);
1853 } break;
1854 case WM_GETDPISCALEDSIZE:
1855 {
1856 SIZE *inoutSize = (SIZE *)lparam;
1857 UINT newDpi = (UINT)wparam; // TODO: WARNING: Converting from WPARAM = UINT_PTR
1858
1859 // for any of these other cases, we might want to post a window
1860 // resize event after the dpi changes?
1861 if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return TRUE;
1862 if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) return TRUE;
1863 if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) return TRUE;
1864
1865 float dpiScale = ((float)newDpi)/96.0f;
1866 bool dpiScaling = CORE.Window.flags & FLAG_WINDOW_HIGHDPI;
1867 // Get size in pixels from points
1868 SIZE desired = {
1869 .cx = dpiScaling? (int)((float)platform.appScreenWidth*dpiScale) : platform.appScreenWidth,
1870 .cy = dpiScaling? (int)((float)platform.appScreenHeight*dpiScale) : platform.appScreenHeight
1871 };
1872 inoutSize->cx = desired.cx;
1873 inoutSize->cy = desired.cy;
1874
1875 result = TRUE;
1876 } break;
1877 case WM_DPICHANGED:
1878 {
1879 // Get current dpi scale factor
1880 float scalex = HIWORD(wParam)/96.0f;
1881 float scaley = LOWORD(wParam)/96.0f;
1882
1883 RECT *suggestedRect = (RECT *)lparam;
1884
1885 // Never set the window size to anything other than the suggested rect here
1886 // Doing so can cause a window to stutter between monitors when transitioning between them
1887 int result = (int)SetWindowPos(hwnd, NULL,
1888 suggestedRect->left, suggestedRect->top,
1889 suggestedRect->right - suggestedRect->left,
1890 suggestedRect->bottom - suggestedRect->top,
1891 SWP_NOZORDER | SWP_NOACTIVATE);
1892
1893 if (result == 0) TRACELOG(LOG_ERROR, "Failed to set window position [ERROR: %lu]", GetLastError());
1894
1895 // TODO: Update screen data, render size, screen scaling, viewport...
1896
1897 } break;
1898 case WM_SETCURSOR:
1899 {
1900 // Called when mouse moves, enters/leaves window...
1901 if (LOWORD(lparam) == HTCLIENT)
1902 {
1903 SetCursor(CORE.Input.Mouse.cursorHidden? NULL : LoadCursorW(NULL, (LPCWSTR)IDC_ARROW));
1904 return 0;
1905 }
1906
1907 result = DefWindowProc(hwnd, msg, wparam, lparam);
1908 } break;
1909 case WM_PAINT:
1910 {
1911 if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer
1912 {
1913 PAINTSTRUCT ps = { 0 };
1914 HDC hdc = BeginPaint(hwnd, &ps);
1915
1916 // Blit from memory DC to window DC
1917 BitBlt(hdc, 0, 0, platform.appScreenWidth, platform.appScreenHeight, platform.hdcmem, 0, 0, SRCCOPY);
1918
1919 EndPaint(hwnd, &ps);
1920 }
1921 }
1922 case WM_INPUT:
1923 {
1924 //HandleRawInput(lparam);
1925 } break;
1926 case WM_MOUSEMOVE:
1927 {
1928 if (!CORE.Input.Mouse.cursorLocked)
1929 {
1930 CORE.Input.Mouse.currentPosition.x = (float)GET_X_LPARAM(lparam);
1931 CORE.Input.Mouse.currentPosition.y = (float)GET_Y_LPARAM(lparam);
1932 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
1933 }
1934 } break;
1935 case WM_KEYDOWN: HandleKey(wparam, lparam, 1); break;
1936 case WM_KEYUP: HandleKey(wparam, lparam, 0); break;
1937 case WM_LBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_LEFT, 1); break;
1938 case WM_LBUTTONUP : HandleMouseButton(MOUSE_BUTTON_LEFT, 0); break;
1939 case WM_RBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_RIGHT, 1); break;
1940 case WM_RBUTTONUP : HandleMouseButton(MOUSE_BUTTON_RIGHT, 0); break;
1941 case WM_MBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_MIDDLE, 1); break;
1942 case WM_MBUTTONUP : HandleMouseButton(MOUSE_BUTTON_MIDDLE, 0); break;
1943 case WM_XBUTTONDOWN:
1944 {
1945 switch (HIWORD(wparam))
1946 {
1947 case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 1); break;
1948 case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 1); break;
1949 default: TRACELOG(LOG_WARNING, "TODO: handle ex mouse button DOWN wparam=%u", HIWORD(wparam)); break;
1950 }
1951 } break;
1952 case WM_XBUTTONUP:
1953 {
1954 switch (HIWORD(wparam))
1955 {
1956 case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 0); break;
1957 case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 0); break;
1958 default: TRACELOG(LOG_WARNING, "TODO: handle ex mouse button UP wparam=%u", HIWORD(wparam)); break;
1959 }
1960 } break;
1961 case WM_MOUSEWHEEL: CORE.Input.Mouse.currentWheelMove.y = ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; break;
1962 case WM_MOUSEHWHEEL: CORE.Input.Mouse.currentWheelMove.x = ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; break;
1963 case WM_APP_UPDATE_WINDOW_SIZE:
1964 {
1965 //UpdateWindowSize(UPDATE_WINDOW_NORMAL, hwnd, platform.appScreenWidth, platform.appScreenHeight, CORE.Window.flags);
1966 } break;
1967
1968 default: result = DefWindowProcW(hwnd, msg, wparam, lparam); // Message passed directly for execution (default behaviour)
1969 }
1970 //------------------------------------------------------------------------------------
1971
1972 // Sanity check for flags
1973 if (platform.hwnd == hwnd) CheckFlags("After WndProc", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), mask);
1974
1975 // Operations to execute after the above check
1976 if (flagsOp.set & flagsOp.clear) TRACELOG(LOG_WARNING, "WIN32: FLAGS: Flags 0x%x were both set and cleared", flagsOp.set & flagsOp.clear);
1977
1978 DWORD save = CORE.Window.flags;
1979 CORE.Window.flags |= flagsOp.set;
1980 CORE.Window.flags &= ~flagsOp.clear;
1981 if (save != CORE.Window.flags) TRACELOG(LOG_DEBUG, "WIN32: FLAGS: Current deferred flags: 0x%x > 0x%x (diff 0x%x)", save, CORE.Window.flags, save ^ CORE.Window.flags);
1982
1983 return result;
1984}
1985
1986// Handle keyboard input event
1987static void HandleKey(WPARAM wparam, LPARAM lparam, char state)
1988{
1989 KeyboardKey key = GetKeyFromWparam(wparam);
1990
1991 // TODO: Use scancode?
1992 //BYTE scancode = lparam >> 16;
1993 //TRACELOG(LOG_INFO, "KEY key=%d vk=%lu scan=%u = %u", key, wparam, scancode, state);
1994
1995 if (key != KEY_NULL)
1996 {
1997 CORE.Input.Keyboard.currentKeyState[key] = state;
1998
1999 if ((key == KEY_ESCAPE) && (state == 1)) CORE.Window.shouldClose = true;
2000 }
2001 else TRACELOG(LOG_WARNING, "INPUT: Unknown (or currently unhandled) virtual keycode %d (0x%x)", wparam, wparam);
2002
2003 // TODO: Add key to the queue as well?
2004}
2005
2006// Handle mouse button input event
2007static void HandleMouseButton(int button, char state)
2008{
2009 // Register current mouse button state
2010 CORE.Input.Mouse.currentButtonState[button] = state;
2011 CORE.Input.Touch.currentTouchState[button] = state;
2012}
2013
2014// Handle raw input event
2015static void HandleRawInput(LPARAM lparam)
2016{
2017 RAWINPUT input = { 0 };
2018
2019 UINT inputSize = sizeof(input);
2020 UINT size = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &input, &inputSize, sizeof(RAWINPUTHEADER));
2021
2022 if (size == (UINT)-1) TRACELOG(LOG_ERROR, "WIN32: Failed to get raw input data [ERROR: %lu]", GetLastError());
2023
2024 if (input.header.dwType != RIM_TYPEMOUSE) TRACELOG(LOG_ERROR, "WIN32: Unexpected WM_INPUT type %lu", input.header.dwType);
2025
2026 if (input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) TRACELOG(LOG_ERROR, "TODO: handle absolute mouse inputs!");
2027
2028 if (input.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) TRACELOG(LOG_ERROR, "TODO: handle virtual desktop mouse inputs!");
2029
2030 // Trick to keep the mouse position at 0,0 and instead move
2031 // the previous position so we can still get a proper mouse delta
2032 //CORE.Input.Mouse.previousPosition.x -= input.data.mouse.lLastX;
2033 //CORE.Input.Mouse.previousPosition.y -= input.data.mouse.lLastY;
2034 //if (CORE.Input.Mouse.currentPosition.x != 0) abort();
2035 //if (CORE.Input.Mouse.currentPosition.y != 0) abort();
2036}
2037
2038// Handle window resizing event
2039static void HandleWindowResize(HWND hwnd, int *width, int *height)
2040{
2041 if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return;
2042
2043 // Get client size (framebuffer inside the window)
2044 RECT rect = { 0 };
2045 GetClientRect(hwnd, &rect);
2046 SIZE clientSize = { rect.right, rect.bottom };
2047
2048 // TODO: Update framebuffer on resize
2049 CORE.Window.currentFbo.width = (int)clientSize.cx;
2050 CORE.Window.currentFbo.height = (int)clientSize.cy;
2051 //SetupViewport(0, 0, clientSize.cx, clientSize.cy);
2052
2053 SetupViewport(clientSize.cx, clientSize.cy);
2054 CORE.Window.resizedLastFrame = true;
2055 float dpiScale = ((float)GetDpiForWindow(hwnd))/96.0f;
2056 bool highdpi = !!(CORE.Window.flags & FLAG_WINDOW_HIGHDPI);
2057 unsigned int screenWidth = highdpi? (unsigned int)(((float)clientSize.cx)/dpiScale) : clientSize.cx;
2058 unsigned int screenHeight = highdpi? (unsigned int)(((float)clientSize.cy)/dpiScale) : clientSize.cy;
2059 CORE.Window.screen.width = screenWidth;
2060 CORE.Window.screen.height = screenHeight;
2061
2062 if (AdoptWindowResize(CORE.Window.flags))
2063 {
2064 TRACELOG(LOG_DEBUG, "WIN32: WINDOW: Updating app size to [%ix%i] from window resize", screenWidth, screenHeight);
2065 *width = screenWidth;
2066 *height = screenHeight;
2067 }
2068
2069 CORE.Window.screenScale = MatrixScale( (float)CORE.Window.render.width/CORE.Window.screen.width,
2070 (float)CORE.Window.render.height/CORE.Window.screen.height, 1.0f);
2071}
2072
2073// Update window style
2074static void UpdateWindowStyle(HWND hwnd, unsigned desiredFlags)
2075{
2076 DWORD current = STYLE_MASK_WRITABLE & MakeWindowStyle(CORE.Window.flags);
2077 DWORD desired = STYLE_MASK_WRITABLE & MakeWindowStyle(desiredFlags);
2078
2079 if (current != desired)
2080 {
2081 SetLastError(0);
2082 DWORD previous = STYLE_MASK_WRITABLE & SetWindowLongPtrW(hwnd, GWL_STYLE, desired);
2083 if (previous != current)
2084 {
2085 TRACELOG(LOG_ERROR, "WIN32: WINDOW: SetWindowLongPtr() returned writable flags 0x%x but expected 0x%x (diff=0x%x, error=%lu)",
2086 previous, current, previous ^ current, GetLastError());
2087 }
2088
2089 CheckFlags("UpdateWindowStyle", hwnd, desiredFlags, desired, STYLE_MASK_WRITABLE);
2090 }
2091
2092 // Minimized takes precedence over maximized
2093 Mized currentMized = MIZED_NONE;
2094 Mized desiredMized = MIZED_NONE;
2095 if (CORE.Window.flags & WS_MINIMIZE) currentMized = MIZED_MIN;
2096 else if (CORE.Window.flags & WS_MAXIMIZE) currentMized = MIZED_MAX;
2097 if (desiredFlags & WS_MINIMIZE) currentMized = MIZED_MIN;
2098 else if (desiredFlags & WS_MAXIMIZE) currentMized = MIZED_MAX;
2099
2100 if (currentMized != desiredMized)
2101 {
2102 switch (desiredMized)
2103 {
2104 case MIZED_NONE: ShowWindow(hwnd, SW_RESTORE); break;
2105 case MIZED_MIN: ShowWindow(hwnd, SW_MINIMIZE); break;
2106 case MIZED_MAX: ShowWindow(hwnd, SW_MAXIMIZE); break;
2107 }
2108 }
2109}
2110
2111// Sanitize flags
2112static unsigned SanitizeFlags(int mode, unsigned flags)
2113{
2114 if ((flags & FLAG_WINDOW_MAXIMIZED) && (flags & FLAG_BORDERLESS_WINDOWED_MODE))
2115 {
2116 TRACELOG(LOG_WARNING, "WIN32: WINDOW: Borderless windows mode overriding maximized window flag");
2117 flags &= ~FLAG_WINDOW_MAXIMIZED;
2118 }
2119
2120 if (mode == 1)
2121 {
2122 if ((flags & FLAG_MSAA_4X_HINT) && (!(CORE.Window.flags & FLAG_MSAA_4X_HINT)))
2123 {
2124 TRACELOG(LOG_WARNING, "WIN32: WINDOW: MSAA can only be configured before window initialization");
2125 flags &= ~FLAG_MSAA_4X_HINT;
2126 }
2127 }
2128
2129 return flags;
2130}
2131
2132// All window state changes from raylib flags go through this function. It performs
2133// whatever operations are needed to update the window state to match the desired flags
2134// In most cases this function should not update CORE.Window.flags directly, instead,
2135// the window itself should update CORE.Window.flags in response to actual state changes
2136// This means that CORE.Window.flags should always represent the actual state of the
2137// window. This function will continue to perform these update operations so long as
2138// the state continues to change
2139//
2140// This design takes care of many odd corner cases. For example, if you want to restore
2141// a window that was previously maximized AND minimized and you want to remove both these
2142// flags, you actually need to call ShowWindow with SW_RESTORE twice. Another example is
2143// if you have a maximized window, if the undecorated flag is modified then we'd need to
2144// update the window style, but updating the style would mean the window size would change
2145// causing the window to lose its Maximized state which would mean we'd need to update the
2146// window size and then update the window style a second time to restore that maximized
2147// state. This implementation is able to handle any/all of these special situations with a
2148// retry loop that continues until we either reach the desired state or the state stops changing
2149static void UpdateFlags(HWND hwnd, unsigned desiredFlags, int width, int height)
2150{
2151 // Flags that just apply immediately without needing any operations
2152 CORE.Window.flags |= (desiredFlags & FLAG_MASK_NO_UPDATE);
2153
2154 int vsync = (CORE.Window.flags & FLAG_VSYNC_HINT)? 1 : 0;
2155 if (wglSwapIntervalEXT)
2156 {
2157 (*wglSwapIntervalEXT)(vsync);
2158 if (vsync) CORE.Window.flags |= FLAG_VSYNC_HINT;
2159 else CORE.Window.flags &= ~FLAG_VSYNC_HINT;
2160 }
2161
2162 // TODO: Review all this code...
2163 DWORD previousStyle = 0;
2164 for (unsigned attempt = 1; ; attempt++)
2165 {
2166 CheckFlags("UpdateFlags", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), STYLE_MASK_ALL);
2167
2168 bool windowSizeUpdated = false;
2169 if (MakeWindowStyle(CORE.Window.flags) == MakeWindowStyle(desiredFlags))
2170 {
2171 windowSizeUpdated = UpdateWindowSize(1, hwnd, width, height, desiredFlags);
2172 if ((FLAG_MASK_REQUIRED & desiredFlags) == (FLAG_MASK_REQUIRED & CORE.Window.flags)) break;
2173 }
2174
2175
2176 if ((attempt > 1) && (previousStyle == MakeWindowStyle(CORE.Window.flags)) && !windowSizeUpdated)
2177 {
2178 TRACELOG(LOG_ERROR, "WIN32: WINDOW: UpdateFlags() failed after %u attempt(s) wanted 0x%x but is 0x%x (diff=0x%x)",
2179 attempt, desiredFlags, CORE.Window.flags, desiredFlags ^ CORE.Window.flags);
2180 }
2181
2182 previousStyle = MakeWindowStyle(CORE.Window.flags);
2183 UpdateWindowStyle(hwnd, desiredFlags);
2184 }
2185}
2186
2187// Check if OpenGL extension is available
2188static bool IsWglExtensionAvailable(HDC hdc, const char *extension)
2189{
2190 bool result = false;
2191
2192 if (wglGetExtensionsStringARB != NULL)
2193 {
2194 const char *extList = wglGetExtensionsStringARB(hdc);
2195 if (extList != NULL)
2196 {
2197 // Simple substring search (could use strtok or strstr)
2198 if (strstr(extList, extension) != NULL) result = true;
2199 }
2200 }
2201
2202 return result;
2203}
Copyright 2026  E766CB298A6D1E64 | Git-Thing heavily inspired by cgit