rcore_desktop_glfw.c (79876B)
1 /********************************************************************************************** 2 * 3 * rcore_desktop - Functions to manage window, graphics device and inputs 4 * 5 * PLATFORM: DESKTOP: GLFW 6 * - Windows (Win32, Win64) 7 * - Linux (X11/Wayland desktop mode) 8 * - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) 9 * - OSX/macOS (x64, arm64) 10 * 11 * LIMITATIONS: 12 * - Limitation 01 13 * - Limitation 02 14 * 15 * POSSIBLE IMPROVEMENTS: 16 * - Improvement 01 17 * - Improvement 02 18 * 19 * ADDITIONAL NOTES: 20 * - TRACELOG() function is located in raylib [utils] module 21 * 22 * CONFIGURATION: 23 * #define RCORE_PLATFORM_CUSTOM_FLAG 24 * Custom flag for rcore on target platform -not used- 25 * 26 * DEPENDENCIES: 27 * - rglfw: Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) 28 * - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) 29 * 30 * 31 * LICENSE: zlib/libpng 32 * 33 * Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors 34 * 35 * This software is provided "as-is", without any express or implied warranty. In no event 36 * will the authors be held liable for any damages arising from the use of this software. 37 * 38 * Permission is granted to anyone to use this software for any purpose, including commercial 39 * applications, and to alter it and redistribute it freely, subject to the following restrictions: 40 * 41 * 1. The origin of this software must not be misrepresented; you must not claim that you 42 * wrote the original software. If you use this software in a product, an acknowledgment 43 * in the product documentation would be appreciated but is not required. 44 * 45 * 2. Altered source versions must be plainly marked as such, and must not be misrepresented 46 * as being the original software. 47 * 48 * 3. This notice may not be removed or altered from any source distribution. 49 * 50 **********************************************************************************************/ 51 52 #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 53 // NOTE: Already provided by rlgl implementation (on glad.h) 54 #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management 55 // NOTE: GLFW3 already includes gl.h (OpenGL) headers 56 57 // Support retrieving native window handlers 58 #if defined(_WIN32) 59 typedef void *PVOID; 60 typedef PVOID HANDLE; 61 #include "../external/win32_clipboard.h" 62 typedef HANDLE HWND; 63 #define GLFW_EXPOSE_NATIVE_WIN32 64 #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h 65 #include "GLFW/glfw3native.h" 66 67 #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) 68 // NOTE: Those functions require linking with winmm library 69 //#pragma warning(disable: 4273) 70 __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); 71 //#pragma warning(default: 4273) 72 #endif 73 #endif 74 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) 75 #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX 76 77 //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type 78 //#define GLFW_EXPOSE_NATIVE_WAYLAND 79 #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window() 80 #endif 81 #if defined(__APPLE__) 82 #include <unistd.h> // Required for: usleep() 83 84 //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition 85 void *glfwGetCocoaWindow(GLFWwindow* handle); 86 #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() 87 #endif 88 89 //---------------------------------------------------------------------------------- 90 // Types and Structures Definition 91 //---------------------------------------------------------------------------------- 92 typedef struct { 93 GLFWwindow *handle; // GLFW window handle (graphic device) 94 } PlatformData; 95 96 //---------------------------------------------------------------------------------- 97 // Global Variables Definition 98 //---------------------------------------------------------------------------------- 99 extern CoreData CORE; // Global CORE state context 100 101 static PlatformData platform = { 0 }; // Platform specific data 102 103 //---------------------------------------------------------------------------------- 104 // Module Internal Functions Declaration 105 //---------------------------------------------------------------------------------- 106 int InitPlatform(void); // Initialize platform (graphics, inputs and more) 107 void ClosePlatform(void); // Close platform 108 109 // Error callback event 110 static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error 111 112 // Window callbacks events 113 static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized 114 static void WindowPosCallback(GLFWwindow* window, int x, int y); // GLFW3 WindowPos Callback, runs when window is moved 115 static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored 116 static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized 117 static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus 118 static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window 119 static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale 120 121 // Input callbacks events 122 static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed 123 static void CharCallback(GLFWwindow *window, unsigned int codepoint); // GLFW3 Char Callback, runs on key pressed (get codepoint value) 124 static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed 125 static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move 126 static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Scrolling Callback, runs on mouse wheel 127 static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area 128 static void JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback 129 130 //---------------------------------------------------------------------------------- 131 // Module Functions Declaration 132 //---------------------------------------------------------------------------------- 133 // NOTE: Functions declaration is provided by raylib.h 134 135 //---------------------------------------------------------------------------------- 136 // Module Functions Definition: Window and Graphics Device 137 //---------------------------------------------------------------------------------- 138 139 // Check if application should close 140 // NOTE: By default, if KEY_ESCAPE pressed or window close icon clicked 141 bool WindowShouldClose(void) 142 { 143 if (CORE.Window.ready) return CORE.Window.shouldClose; 144 else return true; 145 } 146 147 // Toggle fullscreen mode 148 void ToggleFullscreen(void) 149 { 150 if (!CORE.Window.fullscreen) 151 { 152 // Store previous window position (in case we exit fullscreen) 153 CORE.Window.previousPosition = CORE.Window.position; 154 155 int monitorCount = 0; 156 int monitorIndex = GetCurrentMonitor(); 157 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 158 159 // Use current monitor, so we correctly get the display the window is on 160 GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL; 161 162 if (monitor == NULL) 163 { 164 TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); 165 166 CORE.Window.fullscreen = false; 167 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; 168 169 glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); 170 } 171 else 172 { 173 CORE.Window.fullscreen = true; 174 CORE.Window.flags |= FLAG_FULLSCREEN_MODE; 175 176 glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); 177 } 178 179 } 180 else 181 { 182 CORE.Window.fullscreen = false; 183 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; 184 185 glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); 186 187 // we update the window position right away 188 CORE.Window.position.x = CORE.Window.previousPosition.x; 189 CORE.Window.position.y = CORE.Window.previousPosition.y; 190 } 191 192 // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) 193 // NOTE: V-Sync can be enabled by graphic driver configuration 194 if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1); 195 } 196 197 // Toggle borderless windowed mode 198 void ToggleBorderlessWindowed(void) 199 { 200 // Leave fullscreen before attempting to set borderless windowed mode 201 bool wasOnFullscreen = false; 202 if (CORE.Window.fullscreen) 203 { 204 // fullscreen already saves the previous position so it does not need to be set here again 205 ToggleFullscreen(); 206 wasOnFullscreen = true; 207 } 208 209 const int monitor = GetCurrentMonitor(); 210 int monitorCount; 211 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 212 213 if ((monitor >= 0) && (monitor < monitorCount)) 214 { 215 const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); 216 217 if (mode) 218 { 219 if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) 220 { 221 // Store screen position and size 222 // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here 223 if (!wasOnFullscreen) CORE.Window.previousPosition = CORE.Window.position; 224 CORE.Window.previousScreen = CORE.Window.screen; 225 226 // Set undecorated and topmost modes and flags 227 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); 228 CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; 229 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE); 230 CORE.Window.flags |= FLAG_WINDOW_TOPMOST; 231 232 // Get monitor position and size 233 int monitorPosX = 0; 234 int monitorPosY = 0; 235 glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY); 236 const int monitorWidth = mode->width; 237 const int monitorHeight = mode->height; 238 239 // Set screen position and size 240 glfwSetWindowPos(platform.handle, monitorPosX, monitorPosY); 241 glfwSetWindowSize(platform.handle, monitorWidth, monitorHeight); 242 243 // Refocus window 244 glfwFocusWindow(platform.handle); 245 246 CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; 247 } 248 else 249 { 250 // Remove topmost and undecorated modes and flags 251 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE); 252 CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; 253 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); 254 CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; 255 256 // Return previous screen size and position 257 // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly 258 glfwSetWindowSize(platform.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); 259 glfwSetWindowPos(platform.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); 260 261 // Refocus window 262 glfwFocusWindow(platform.handle); 263 264 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; 265 266 CORE.Window.position.x = CORE.Window.previousPosition.x; 267 CORE.Window.position.y = CORE.Window.previousPosition.y; 268 } 269 } 270 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); 271 } 272 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); 273 } 274 275 // Set window state: maximized, if resizable 276 void MaximizeWindow(void) 277 { 278 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) 279 { 280 glfwMaximizeWindow(platform.handle); 281 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; 282 } 283 } 284 285 // Set window state: minimized 286 void MinimizeWindow(void) 287 { 288 // NOTE: Following function launches callback that sets appropriate flag! 289 glfwIconifyWindow(platform.handle); 290 } 291 292 // Set window state: not minimized/maximized 293 void RestoreWindow(void) 294 { 295 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) 296 { 297 // Restores the specified window if it was previously iconified (minimized) or maximized 298 glfwRestoreWindow(platform.handle); 299 CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; 300 CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; 301 } 302 } 303 304 // Set window configuration state using flags 305 void SetWindowState(unsigned int flags) 306 { 307 // Check previous state and requested state to apply required changes 308 // NOTE: In most cases the functions already change the flags internally 309 310 // State change: FLAG_VSYNC_HINT 311 if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0)) 312 { 313 glfwSwapInterval(1); 314 CORE.Window.flags |= FLAG_VSYNC_HINT; 315 } 316 317 // State change: FLAG_BORDERLESS_WINDOWED_MODE 318 // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running 319 if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) 320 { 321 ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function 322 } 323 324 // State change: FLAG_FULLSCREEN_MODE 325 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE)) 326 { 327 ToggleFullscreen(); // NOTE: Window state flag updated inside function 328 } 329 330 // State change: FLAG_WINDOW_RESIZABLE 331 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) 332 { 333 glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE); 334 CORE.Window.flags |= FLAG_WINDOW_RESIZABLE; 335 } 336 337 // State change: FLAG_WINDOW_UNDECORATED 338 if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED)) 339 { 340 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); 341 CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; 342 } 343 344 // State change: FLAG_WINDOW_HIDDEN 345 if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) 346 { 347 glfwHideWindow(platform.handle); 348 CORE.Window.flags |= FLAG_WINDOW_HIDDEN; 349 } 350 351 // State change: FLAG_WINDOW_MINIMIZED 352 if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) 353 { 354 //GLFW_ICONIFIED 355 MinimizeWindow(); // NOTE: Window state flag updated inside function 356 } 357 358 // State change: FLAG_WINDOW_MAXIMIZED 359 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) 360 { 361 //GLFW_MAXIMIZED 362 MaximizeWindow(); // NOTE: Window state flag updated inside function 363 } 364 365 // State change: FLAG_WINDOW_UNFOCUSED 366 if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) 367 { 368 glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); 369 CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; 370 } 371 372 // State change: FLAG_WINDOW_TOPMOST 373 if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) 374 { 375 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE); 376 CORE.Window.flags |= FLAG_WINDOW_TOPMOST; 377 } 378 379 // State change: FLAG_WINDOW_ALWAYS_RUN 380 if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) 381 { 382 CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN; 383 } 384 385 // The following states can not be changed after window creation 386 387 // State change: FLAG_WINDOW_TRANSPARENT 388 if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) 389 { 390 TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); 391 } 392 393 // State change: FLAG_WINDOW_HIGHDPI 394 if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) 395 { 396 TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); 397 } 398 399 // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH 400 if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) 401 { 402 glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); 403 CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH; 404 } 405 406 // State change: FLAG_MSAA_4X_HINT 407 if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0)) 408 { 409 TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); 410 } 411 412 // State change: FLAG_INTERLACED_HINT 413 if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0)) 414 { 415 TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); 416 } 417 } 418 419 // Clear window configuration state flags 420 void ClearWindowState(unsigned int flags) 421 { 422 // Check previous state and requested state to apply required changes 423 // NOTE: In most cases the functions already change the flags internally 424 425 // State change: FLAG_VSYNC_HINT 426 if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0)) 427 { 428 glfwSwapInterval(0); 429 CORE.Window.flags &= ~FLAG_VSYNC_HINT; 430 } 431 432 // State change: FLAG_BORDERLESS_WINDOWED_MODE 433 // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running 434 if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) 435 { 436 ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function 437 } 438 439 // State change: FLAG_FULLSCREEN_MODE 440 if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0)) 441 { 442 ToggleFullscreen(); // NOTE: Window state flag updated inside function 443 } 444 445 // State change: FLAG_WINDOW_RESIZABLE 446 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) 447 { 448 glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE); 449 CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; 450 } 451 452 // State change: FLAG_WINDOW_HIDDEN 453 if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) 454 { 455 glfwShowWindow(platform.handle); 456 CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; 457 } 458 459 // State change: FLAG_WINDOW_MINIMIZED 460 if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) 461 { 462 RestoreWindow(); // NOTE: Window state flag updated inside function 463 } 464 465 // State change: FLAG_WINDOW_MAXIMIZED 466 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) 467 { 468 RestoreWindow(); // NOTE: Window state flag updated inside function 469 } 470 471 // State change: FLAG_WINDOW_UNDECORATED 472 if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) 473 { 474 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); 475 CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; 476 } 477 478 // State change: FLAG_WINDOW_UNFOCUSED 479 if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) 480 { 481 glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); 482 CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; 483 } 484 485 // State change: FLAG_WINDOW_TOPMOST 486 if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) 487 { 488 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE); 489 CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; 490 } 491 492 // State change: FLAG_WINDOW_ALWAYS_RUN 493 if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) 494 { 495 CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN; 496 } 497 498 // The following states can not be changed after window creation 499 500 // State change: FLAG_WINDOW_TRANSPARENT 501 if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) 502 { 503 TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); 504 } 505 506 // State change: FLAG_WINDOW_HIGHDPI 507 if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) 508 { 509 TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); 510 } 511 512 // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH 513 if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) 514 { 515 glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); 516 CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH; 517 } 518 519 // State change: FLAG_MSAA_4X_HINT 520 if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0)) 521 { 522 TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); 523 } 524 525 // State change: FLAG_INTERLACED_HINT 526 if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0)) 527 { 528 TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); 529 } 530 } 531 532 // Set icon for window 533 // NOTE 1: Image must be in RGBA format, 8bit per channel 534 // NOTE 2: Image is scaled by the OS for all required sizes 535 void SetWindowIcon(Image image) 536 { 537 if (image.data == NULL) 538 { 539 // Revert to the default window icon, pass in an empty image array 540 glfwSetWindowIcon(platform.handle, 0, NULL); 541 } 542 else 543 { 544 if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) 545 { 546 GLFWimage icon[1] = { 0 }; 547 548 icon[0].width = image.width; 549 icon[0].height = image.height; 550 icon[0].pixels = (unsigned char *)image.data; 551 552 // NOTE 1: We only support one image icon 553 // NOTE 2: The specified image data is copied before this function returns 554 glfwSetWindowIcon(platform.handle, 1, icon); 555 } 556 else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); 557 } 558 } 559 560 // Set icon for window, multiple images 561 // NOTE 1: Images must be in RGBA format, 8bit per channel 562 // NOTE 2: The multiple images are used depending on provided sizes 563 // Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 564 void SetWindowIcons(Image *images, int count) 565 { 566 if ((images == NULL) || (count <= 0)) 567 { 568 // Revert to the default window icon, pass in an empty image array 569 glfwSetWindowIcon(platform.handle, 0, NULL); 570 } 571 else 572 { 573 int valid = 0; 574 GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); 575 576 for (int i = 0; i < count; i++) 577 { 578 if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) 579 { 580 icons[valid].width = images[i].width; 581 icons[valid].height = images[i].height; 582 icons[valid].pixels = (unsigned char *)images[i].data; 583 584 valid++; 585 } 586 else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); 587 } 588 // NOTE: Images data is copied internally before this function returns 589 glfwSetWindowIcon(platform.handle, valid, icons); 590 591 RL_FREE(icons); 592 } 593 } 594 595 // Set title for window 596 void SetWindowTitle(const char *title) 597 { 598 CORE.Window.title = title; 599 glfwSetWindowTitle(platform.handle, title); 600 } 601 602 // Set window position on screen (windowed mode) 603 void SetWindowPosition(int x, int y) 604 { 605 // Update CORE.Window.position as well 606 CORE.Window.position.x = x; 607 CORE.Window.position.y = y; 608 glfwSetWindowPos(platform.handle, x, y); 609 } 610 611 // Set monitor for the current window 612 void SetWindowMonitor(int monitor) 613 { 614 int monitorCount = 0; 615 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 616 617 if ((monitor >= 0) && (monitor < monitorCount)) 618 { 619 if (CORE.Window.fullscreen) 620 { 621 TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); 622 623 const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); 624 glfwSetWindowMonitor(platform.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); 625 } 626 else 627 { 628 TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); 629 630 // Here the render width has to be used again in case high dpi flag is enabled 631 const int screenWidth = CORE.Window.render.width; 632 const int screenHeight = CORE.Window.render.height; 633 int monitorWorkareaX = 0; 634 int monitorWorkareaY = 0; 635 int monitorWorkareaWidth = 0; 636 int monitorWorkareaHeight = 0; 637 glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); 638 639 // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it 640 if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(platform.handle, monitorWorkareaX, monitorWorkareaY); 641 else 642 { 643 const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); 644 const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); 645 glfwSetWindowPos(platform.handle, x, y); 646 } 647 } 648 } 649 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); 650 } 651 652 // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) 653 void SetWindowMinSize(int width, int height) 654 { 655 CORE.Window.screenMin.width = width; 656 CORE.Window.screenMin.height = height; 657 658 int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; 659 int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; 660 int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; 661 int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; 662 663 glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); 664 } 665 666 // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) 667 void SetWindowMaxSize(int width, int height) 668 { 669 CORE.Window.screenMax.width = width; 670 CORE.Window.screenMax.height = height; 671 672 int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; 673 int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; 674 int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; 675 int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; 676 677 glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); 678 } 679 680 // Set window dimensions 681 void SetWindowSize(int width, int height) 682 { 683 CORE.Window.screen.width = width; 684 CORE.Window.screen.height = height; 685 686 glfwSetWindowSize(platform.handle, width, height); 687 } 688 689 // Set window opacity, value opacity is between 0.0 and 1.0 690 void SetWindowOpacity(float opacity) 691 { 692 if (opacity >= 1.0f) opacity = 1.0f; 693 else if (opacity <= 0.0f) opacity = 0.0f; 694 glfwSetWindowOpacity(platform.handle, opacity); 695 } 696 697 // Set window focused 698 void SetWindowFocused(void) 699 { 700 glfwFocusWindow(platform.handle); 701 } 702 703 // Get native window handle 704 void *GetWindowHandle(void) 705 { 706 #if defined(_WIN32) 707 // NOTE: Returned handle is: void *HWND (windows.h) 708 return glfwGetWin32Window(platform.handle); 709 #endif 710 #if defined(__linux__) 711 // NOTE: Returned handle is: unsigned long Window (X.h) 712 // typedef unsigned long XID; 713 // typedef XID Window; 714 //unsigned long id = (unsigned long)glfwGetX11Window(platform.handle); 715 //return NULL; // TODO: Find a way to return value... cast to void *? 716 return (void *)platform.handle; 717 #endif 718 #if defined(__APPLE__) 719 // NOTE: Returned handle is: (objc_object *) 720 return (void *)glfwGetCocoaWindow(platform.handle); 721 #endif 722 723 return NULL; 724 } 725 726 // Get number of monitors 727 int GetMonitorCount(void) 728 { 729 int monitorCount = 0; 730 731 glfwGetMonitors(&monitorCount); 732 733 return monitorCount; 734 } 735 736 // Get number of monitors 737 int GetCurrentMonitor(void) 738 { 739 int index = 0; 740 int monitorCount = 0; 741 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 742 GLFWmonitor *monitor = NULL; 743 744 if (monitorCount >= 1) 745 { 746 if (IsWindowFullscreen()) 747 { 748 // Get the handle of the monitor that the specified window is in full screen on 749 monitor = glfwGetWindowMonitor(platform.handle); 750 751 for (int i = 0; i < monitorCount; i++) 752 { 753 if (monitors[i] == monitor) 754 { 755 index = i; 756 break; 757 } 758 } 759 } 760 else 761 { 762 // In case the window is between two monitors, we use below logic 763 // to try to detect the "current monitor" for that window, note that 764 // this is probably an overengineered solution for a very side case 765 // trying to match SDL behaviour 766 767 int closestDist = 0x7FFFFFFF; 768 769 // Window center position 770 int wcx = 0; 771 int wcy = 0; 772 773 glfwGetWindowPos(platform.handle, &wcx, &wcy); 774 wcx += (int)CORE.Window.screen.width/2; 775 wcy += (int)CORE.Window.screen.height/2; 776 777 for (int i = 0; i < monitorCount; i++) 778 { 779 // Monitor top-left position 780 int mx = 0; 781 int my = 0; 782 783 monitor = monitors[i]; 784 glfwGetMonitorPos(monitor, &mx, &my); 785 const GLFWvidmode *mode = glfwGetVideoMode(monitor); 786 787 if (mode) 788 { 789 const int right = mx + mode->width - 1; 790 const int bottom = my + mode->height - 1; 791 792 if ((wcx >= mx) && 793 (wcx <= right) && 794 (wcy >= my) && 795 (wcy <= bottom)) 796 { 797 index = i; 798 break; 799 } 800 801 int xclosest = wcx; 802 if (wcx < mx) xclosest = mx; 803 else if (wcx > right) xclosest = right; 804 805 int yclosest = wcy; 806 if (wcy < my) yclosest = my; 807 else if (wcy > bottom) yclosest = bottom; 808 809 int dx = wcx - xclosest; 810 int dy = wcy - yclosest; 811 int dist = (dx*dx) + (dy*dy); 812 if (dist < closestDist) 813 { 814 index = i; 815 closestDist = dist; 816 } 817 } 818 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); 819 } 820 } 821 } 822 823 return index; 824 } 825 826 // Get selected monitor position 827 Vector2 GetMonitorPosition(int monitor) 828 { 829 int monitorCount = 0; 830 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 831 832 if ((monitor >= 0) && (monitor < monitorCount)) 833 { 834 int x, y; 835 glfwGetMonitorPos(monitors[monitor], &x, &y); 836 837 return (Vector2){ (float)x, (float)y }; 838 } 839 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); 840 return (Vector2){ 0, 0 }; 841 } 842 843 // Get selected monitor width (currently used by monitor) 844 int GetMonitorWidth(int monitor) 845 { 846 int width = 0; 847 int monitorCount = 0; 848 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 849 850 if ((monitor >= 0) && (monitor < monitorCount)) 851 { 852 const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); 853 854 if (mode) width = mode->width; 855 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); 856 } 857 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); 858 859 return width; 860 } 861 862 // Get selected monitor height (currently used by monitor) 863 int GetMonitorHeight(int monitor) 864 { 865 int height = 0; 866 int monitorCount = 0; 867 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 868 869 if ((monitor >= 0) && (monitor < monitorCount)) 870 { 871 const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); 872 873 if (mode) height = mode->height; 874 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); 875 } 876 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); 877 878 return height; 879 } 880 881 // Get selected monitor physical width in millimetres 882 int GetMonitorPhysicalWidth(int monitor) 883 { 884 int width = 0; 885 int monitorCount = 0; 886 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 887 888 if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], &width, NULL); 889 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); 890 891 return width; 892 } 893 894 // Get selected monitor physical height in millimetres 895 int GetMonitorPhysicalHeight(int monitor) 896 { 897 int height = 0; 898 int monitorCount = 0; 899 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 900 901 if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &height); 902 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); 903 904 return height; 905 } 906 907 // Get selected monitor refresh rate 908 int GetMonitorRefreshRate(int monitor) 909 { 910 int refresh = 0; 911 int monitorCount = 0; 912 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 913 914 if ((monitor >= 0) && (monitor < monitorCount)) 915 { 916 const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]); 917 refresh = vidmode->refreshRate; 918 } 919 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); 920 921 return refresh; 922 } 923 924 // Get the human-readable, UTF-8 encoded name of the selected monitor 925 const char *GetMonitorName(int monitor) 926 { 927 int monitorCount = 0; 928 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 929 930 if ((monitor >= 0) && (monitor < monitorCount)) 931 { 932 return glfwGetMonitorName(monitors[monitor]); 933 } 934 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); 935 return ""; 936 } 937 938 // Get window position XY on monitor 939 Vector2 GetWindowPosition(void) 940 { 941 int x = 0; 942 int y = 0; 943 944 glfwGetWindowPos(platform.handle, &x, &y); 945 946 return (Vector2){ (float)x, (float)y }; 947 } 948 949 // Get window scale DPI factor for current monitor 950 Vector2 GetWindowScaleDPI(void) 951 { 952 Vector2 scale = {0}; 953 glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y); 954 return scale; 955 } 956 957 // Set clipboard text content 958 void SetClipboardText(const char *text) 959 { 960 glfwSetClipboardString(platform.handle, text); 961 } 962 963 // Get clipboard text content 964 // NOTE: returned string is allocated and freed by GLFW 965 const char *GetClipboardText(void) 966 { 967 return glfwGetClipboardString(platform.handle); 968 } 969 970 #if defined(SUPPORT_CLIPBOARD_IMAGE) 971 // Get clipboard image 972 Image GetClipboardImage(void) 973 { 974 Image image = {0}; 975 unsigned long long int dataSize = 0; 976 void* fileData = NULL; 977 978 #ifdef _WIN32 979 int width, height; 980 fileData = (void*)Win32GetClipboardImageData(&width, &height, &dataSize); 981 #else 982 TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_GLFW doesn't implement `GetClipboardImage` for this OS"); 983 #endif 984 985 if (fileData == NULL) 986 { 987 TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data."); 988 } 989 else 990 { 991 image = LoadImageFromMemory(".bmp", fileData, (int)dataSize); 992 } 993 return image; 994 } 995 #endif // SUPPORT_CLIPBOARD_IMAGE 996 997 // Show mouse cursor 998 void ShowCursor(void) 999 { 1000 glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 1001 CORE.Input.Mouse.cursorHidden = false; 1002 } 1003 1004 // Hides mouse cursor 1005 void HideCursor(void) 1006 { 1007 glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); 1008 CORE.Input.Mouse.cursorHidden = true; 1009 } 1010 1011 // Enables cursor (unlock cursor) 1012 void EnableCursor(void) 1013 { 1014 glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 1015 1016 // Set cursor position in the middle 1017 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); 1018 1019 if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE); 1020 1021 CORE.Input.Mouse.cursorHidden = false; 1022 } 1023 1024 // Disables cursor (lock cursor) 1025 void DisableCursor(void) 1026 { 1027 glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); 1028 1029 // Set cursor position in the middle 1030 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); 1031 1032 if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); 1033 1034 CORE.Input.Mouse.cursorHidden = true; 1035 } 1036 1037 // Swap back buffer with front buffer (screen drawing) 1038 void SwapScreenBuffer(void) 1039 { 1040 glfwSwapBuffers(platform.handle); 1041 } 1042 1043 //---------------------------------------------------------------------------------- 1044 // Module Functions Definition: Misc 1045 //---------------------------------------------------------------------------------- 1046 1047 // Get elapsed time measure in seconds since InitTimer() 1048 double GetTime(void) 1049 { 1050 double time = glfwGetTime(); // Elapsed time since glfwInit() 1051 return time; 1052 } 1053 1054 // Open URL with default system browser (if available) 1055 // NOTE: This function is only safe to use if you control the URL given. 1056 // A user could craft a malicious string performing another action. 1057 // Only call this function yourself not with user input or make sure to check the string yourself. 1058 // Ref: https://github.com/raysan5/raylib/issues/686 1059 void OpenURL(const char *url) 1060 { 1061 // Security check to (partially) avoid malicious code 1062 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); 1063 else 1064 { 1065 char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char)); 1066 #if defined(_WIN32) 1067 sprintf(cmd, "explorer \"%s\"", url); 1068 #endif 1069 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) 1070 sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser 1071 #endif 1072 #if defined(__APPLE__) 1073 sprintf(cmd, "open '%s'", url); 1074 #endif 1075 int result = system(cmd); 1076 if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created"); 1077 RL_FREE(cmd); 1078 } 1079 } 1080 1081 //---------------------------------------------------------------------------------- 1082 // Module Functions Definition: Inputs 1083 //---------------------------------------------------------------------------------- 1084 1085 // Set internal gamepad mappings 1086 int SetGamepadMappings(const char *mappings) 1087 { 1088 return glfwUpdateGamepadMappings(mappings); 1089 } 1090 1091 // Set gamepad vibration 1092 void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration) 1093 { 1094 TRACELOG(LOG_WARNING, "GamepadSetVibration() not available on target platform"); 1095 } 1096 1097 // Set mouse position XY 1098 void SetMousePosition(int x, int y) 1099 { 1100 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; 1101 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; 1102 1103 // NOTE: emscripten not implemented 1104 glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); 1105 } 1106 1107 // Set mouse cursor 1108 void SetMouseCursor(int cursor) 1109 { 1110 CORE.Input.Mouse.cursor = cursor; 1111 if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(platform.handle, NULL); 1112 else 1113 { 1114 // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values 1115 glfwSetCursor(platform.handle, glfwCreateStandardCursor(0x00036000 + cursor)); 1116 } 1117 } 1118 1119 // Get physical key name. 1120 const char *GetKeyName(int key) 1121 { 1122 return glfwGetKeyName(key, glfwGetKeyScancode(key)); 1123 } 1124 1125 // Register all input events 1126 void PollInputEvents(void) 1127 { 1128 #if defined(SUPPORT_GESTURES_SYSTEM) 1129 // NOTE: Gestures update must be called every frame to reset gestures correctly 1130 // because ProcessGestureEvent() is just called on an event, not every frame 1131 UpdateGestures(); 1132 #endif 1133 1134 // Reset keys/chars pressed registered 1135 CORE.Input.Keyboard.keyPressedQueueCount = 0; 1136 CORE.Input.Keyboard.charPressedQueueCount = 0; 1137 1138 // Reset last gamepad button/axis registered state 1139 CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN 1140 //CORE.Input.Gamepad.axisCount = 0; 1141 1142 // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) 1143 1144 // Register previous keys states 1145 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) 1146 { 1147 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; 1148 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; 1149 } 1150 1151 // Register previous mouse states 1152 for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; 1153 1154 // Register previous mouse wheel state 1155 CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; 1156 CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; 1157 1158 // Register previous mouse position 1159 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; 1160 1161 // Register previous touch states 1162 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; 1163 1164 // Reset touch positions 1165 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; 1166 1167 // Map touch position to mouse position for convenience 1168 // WARNING: If the target desktop device supports touch screen, this behaviour should be reviewed! 1169 // TODO: GLFW does not support multi-touch input just yet 1170 // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch 1171 // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages 1172 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; 1173 1174 // Check if gamepads are ready 1175 // NOTE: We do it here in case of disconnection 1176 for (int i = 0; i < MAX_GAMEPADS; i++) 1177 { 1178 if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; 1179 else CORE.Input.Gamepad.ready[i] = false; 1180 } 1181 1182 // Register gamepads buttons events 1183 for (int i = 0; i < MAX_GAMEPADS; i++) 1184 { 1185 if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available 1186 { 1187 // Register previous gamepad states 1188 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; 1189 1190 // Get current gamepad state 1191 // NOTE: There is no callback available, so we get it manually 1192 GLFWgamepadstate state = { 0 }; 1193 glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller 1194 1195 const unsigned char *buttons = state.buttons; 1196 1197 for (int k = 0; (buttons != NULL) && (k < MAX_GAMEPAD_BUTTONS); k++) 1198 { 1199 int button = -1; // GamepadButton enum values assigned 1200 1201 switch (k) 1202 { 1203 case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; 1204 case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; 1205 case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; 1206 case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; 1207 1208 case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; 1209 case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; 1210 1211 case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; 1212 case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break; 1213 case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; 1214 1215 case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; 1216 case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; 1217 case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; 1218 case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; 1219 1220 case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break; 1221 case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; 1222 default: break; 1223 } 1224 1225 if (button != -1) // Check for valid button 1226 { 1227 if (buttons[k] == GLFW_PRESS) 1228 { 1229 CORE.Input.Gamepad.currentButtonState[i][button] = 1; 1230 CORE.Input.Gamepad.lastButtonPressed = button; 1231 } 1232 else CORE.Input.Gamepad.currentButtonState[i][button] = 0; 1233 } 1234 } 1235 1236 // Get current axis state 1237 const float *axes = state.axes; 1238 1239 for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1); k++) 1240 { 1241 CORE.Input.Gamepad.axisState[i][k] = axes[k]; 1242 } 1243 1244 // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis) 1245 CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f); 1246 CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f); 1247 1248 CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1; 1249 } 1250 } 1251 1252 CORE.Window.resizedLastFrame = false; 1253 1254 if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) 1255 else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) -> Update keys state 1256 1257 // While window minimized, stop loop execution 1258 while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); 1259 1260 CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle); 1261 1262 // Reset close status for next frame 1263 glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); 1264 } 1265 1266 //---------------------------------------------------------------------------------- 1267 // Module Internal Functions Definition 1268 //---------------------------------------------------------------------------------- 1269 1270 static void SetDimensionsFromMonitor(GLFWmonitor *monitor) 1271 { 1272 const GLFWvidmode *mode = glfwGetVideoMode(monitor); 1273 1274 // Default display resolution to that of the current mode 1275 CORE.Window.display.width = mode->width; 1276 CORE.Window.display.height = mode->height; 1277 1278 // Set screen width/height to the display width/height if they are 0 1279 if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; 1280 if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; 1281 } 1282 1283 // Initialize platform: graphics, inputs and more 1284 int InitPlatform(void) 1285 { 1286 glfwSetErrorCallback(ErrorCallback); 1287 /* 1288 // TODO: Setup GLFW custom allocators to match raylib ones 1289 const GLFWallocator allocator = { 1290 .allocate = MemAlloc, 1291 .deallocate = MemFree, 1292 .reallocate = MemRealloc, 1293 .user = NULL 1294 }; 1295 1296 glfwInitAllocator(&allocator); 1297 */ 1298 1299 #if defined(__APPLE__) 1300 glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); 1301 #endif 1302 // Initialize GLFW internal global state 1303 int result = glfwInit(); 1304 if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } 1305 1306 // Initialize graphic device: display/window and graphic context 1307 //---------------------------------------------------------------------------- 1308 glfwDefaultWindowHints(); // Set default windows hints 1309 //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits 1310 //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits 1311 //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits 1312 //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits 1313 //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits 1314 //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window 1315 //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API 1316 //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers 1317 1318 // Disable GlFW auto iconify behaviour 1319 // Auto Iconify automatically minimizes (iconifies) the window if the window loses focus 1320 // additionally auto iconify restores the hardware resolution of the monitor if the window that loses focus is a fullscreen window 1321 glfwWindowHint(GLFW_AUTO_ICONIFY, 0); 1322 1323 // Check window creation flags 1324 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true; 1325 1326 if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window 1327 else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden 1328 1329 if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window 1330 else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window 1331 1332 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window 1333 else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable 1334 1335 // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization 1336 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; 1337 1338 // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization 1339 if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; 1340 1341 if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); 1342 else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); 1343 1344 if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); 1345 else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); 1346 1347 // NOTE: Some GLFW flags are not supported on HTML5 1348 if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer 1349 else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer 1350 1351 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) 1352 { 1353 // Resize window content area based on the monitor content scale. 1354 // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11. 1355 // On platforms like macOS the resolution of the framebuffer is changed independently of the window size. 1356 glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on 1357 #if defined(__APPLE__) 1358 glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); 1359 #endif 1360 } 1361 else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); 1362 1363 // Mouse passthrough 1364 if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); 1365 else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); 1366 1367 if (CORE.Window.flags & FLAG_MSAA_4X_HINT) 1368 { 1369 // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs 1370 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); 1371 glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 1372 } 1373 1374 // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version 1375 // with backward compatibility to older OpenGL versions. 1376 // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context. 1377 1378 // Check selection OpenGL version 1379 if (rlGetVersion() == RL_OPENGL_21) 1380 { 1381 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) 1382 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) 1383 } 1384 else if (rlGetVersion() == RL_OPENGL_33) 1385 { 1386 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) 1387 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) 1388 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! 1389 // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE 1390 #if defined(__APPLE__) 1391 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility 1392 #else 1393 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! 1394 #endif 1395 //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context 1396 } 1397 else if (rlGetVersion() == RL_OPENGL_43) 1398 { 1399 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint) 1400 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) 1401 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 1402 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); 1403 #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) 1404 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context 1405 #endif 1406 } 1407 else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context 1408 { 1409 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); 1410 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 1411 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); 1412 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); 1413 } 1414 else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context 1415 { 1416 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 1417 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); 1418 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); 1419 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); 1420 } 1421 1422 // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions. 1423 // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn. 1424 // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience. 1425 // REF: https://github.com/raysan5/raylib/issues/1554 1426 glfwSetJoystickCallback(NULL); 1427 1428 GLFWmonitor *monitor = NULL; 1429 if (CORE.Window.fullscreen) 1430 { 1431 // According to glfwCreateWindow(), if the user does not have a choice, fullscreen applications 1432 // should default to the primary monitor. 1433 1434 monitor = glfwGetPrimaryMonitor(); 1435 if (!monitor) 1436 { 1437 TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); 1438 return -1; 1439 } 1440 1441 SetDimensionsFromMonitor(monitor); 1442 1443 // Remember center for switching from fullscreen to window 1444 if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) 1445 { 1446 // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. 1447 // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11. 1448 CORE.Window.position.x = CORE.Window.display.width/4; 1449 CORE.Window.position.y = CORE.Window.display.height/4; 1450 } 1451 else 1452 { 1453 CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2; 1454 CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2; 1455 } 1456 1457 if (CORE.Window.position.x < 0) CORE.Window.position.x = 0; 1458 if (CORE.Window.position.y < 0) CORE.Window.position.y = 0; 1459 1460 // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor 1461 int count = 0; 1462 const GLFWvidmode *modes = glfwGetVideoModes(monitor, &count); 1463 1464 // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height 1465 for (int i = 0; i < count; i++) 1466 { 1467 if ((unsigned int)modes[i].width >= CORE.Window.screen.width) 1468 { 1469 if ((unsigned int)modes[i].height >= CORE.Window.screen.height) 1470 { 1471 CORE.Window.display.width = modes[i].width; 1472 CORE.Window.display.height = modes[i].height; 1473 break; 1474 } 1475 } 1476 } 1477 1478 TRACELOG(LOG_INFO, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); 1479 1480 // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, 1481 // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), 1482 // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched 1483 // by the sides to fit all monitor space... 1484 1485 // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight 1486 // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset) 1487 // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale 1488 // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed... 1489 // HighDPI monitors are properly considered in a following similar function: SetupViewport() 1490 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); 1491 1492 platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL); 1493 1494 // NOTE: Full-screen change, not working properly... 1495 //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); 1496 } 1497 else 1498 { 1499 // No-fullscreen window creation 1500 bool requestWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0); 1501 1502 // Default to at least one pixel in size, as creation with a zero dimension is not allowed. 1503 int creationWidth = CORE.Window.screen.width != 0 ? CORE.Window.screen.width : 1; 1504 int creationHeight = CORE.Window.screen.height != 0 ? CORE.Window.screen.height : 1; 1505 1506 platform.handle = glfwCreateWindow(creationWidth, creationHeight, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); 1507 1508 // After the window was created, determine the monitor that the window manager assigned. 1509 // Derive display sizes, and, if possible, window size in case it was zero at beginning. 1510 1511 int monitorCount = 0; 1512 int monitorIndex = GetCurrentMonitor(); 1513 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); 1514 1515 if (monitorIndex < monitorCount) 1516 { 1517 monitor = monitors[monitorIndex]; 1518 SetDimensionsFromMonitor(monitor); 1519 1520 if (requestWindowedFullscreen) glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height); 1521 } 1522 else 1523 { 1524 // The monitor for the window-manager-created window can not be determined, so it can not be centered. 1525 glfwTerminate(); 1526 TRACELOG(LOG_WARNING, "GLFW: Failed to determine Monitor to center Window"); 1527 return -1; 1528 } 1529 1530 if (platform.handle) 1531 { 1532 CORE.Window.render.width = CORE.Window.screen.width; 1533 CORE.Window.render.height = CORE.Window.screen.height; 1534 } 1535 } 1536 1537 if (!platform.handle) 1538 { 1539 glfwTerminate(); 1540 TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); 1541 return -1; 1542 } 1543 1544 glfwMakeContextCurrent(platform.handle); 1545 result = glfwGetError(NULL); 1546 1547 // Check context activation 1548 if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) 1549 { 1550 CORE.Window.ready = true; 1551 1552 glfwSwapInterval(0); // No V-Sync by default 1553 1554 // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) 1555 // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need 1556 // to be activated on web platforms since VSync is enforced there. 1557 if (CORE.Window.flags & FLAG_VSYNC_HINT) 1558 { 1559 // WARNING: It seems to hit a critical render path in Intel HD Graphics 1560 glfwSwapInterval(1); 1561 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); 1562 } 1563 1564 int fbWidth = CORE.Window.screen.width; 1565 int fbHeight = CORE.Window.screen.height; 1566 1567 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) 1568 { 1569 // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. 1570 // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); 1571 #if !defined(__APPLE__) 1572 glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); 1573 1574 // Screen scaling matrix is required in case desired screen area is different from display area 1575 CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); 1576 1577 // Mouse input scaling for the new screen size 1578 SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); 1579 #endif 1580 } 1581 1582 CORE.Window.render.width = fbWidth; 1583 CORE.Window.render.height = fbHeight; 1584 CORE.Window.currentFbo.width = fbWidth; 1585 CORE.Window.currentFbo.height = fbHeight; 1586 1587 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); 1588 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); 1589 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); 1590 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); 1591 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); 1592 } 1593 else 1594 { 1595 TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); 1596 return -1; 1597 } 1598 1599 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); 1600 1601 // If graphic device is no properly initialized, we end program 1602 if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } 1603 else 1604 { 1605 // Try to center window on screen but avoiding window-bar outside of screen 1606 int monitorX = 0; 1607 int monitorY = 0; 1608 int monitorWidth = 0; 1609 int monitorHeight = 0; 1610 glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight); 1611 1612 // Here CORE.Window.render.width/height should be used instead of CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled. 1613 int posX = monitorX + (monitorWidth - (int)CORE.Window.render.width)/2; 1614 int posY = monitorY + (monitorHeight - (int)CORE.Window.render.height)/2; 1615 if (posX < monitorX) posX = monitorX; 1616 if (posY < monitorY) posY = monitorY; 1617 SetWindowPosition(posX, posY); 1618 1619 // Update CORE.Window.position here so it is correct from the start 1620 CORE.Window.position.x = posX; 1621 CORE.Window.position.y = posY; 1622 } 1623 1624 // Load OpenGL extensions 1625 // NOTE: GL procedures address loader is required to load extensions 1626 rlLoadExtensions(glfwGetProcAddress); 1627 //---------------------------------------------------------------------------- 1628 1629 // Initialize input events callbacks 1630 //---------------------------------------------------------------------------- 1631 // Set window callback events 1632 glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! 1633 glfwSetWindowPosCallback(platform.handle, WindowPosCallback); 1634 glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); 1635 glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); 1636 glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); 1637 glfwSetDropCallback(platform.handle, WindowDropCallback); 1638 1639 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) 1640 { 1641 glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback); 1642 } 1643 1644 // Set input callback events 1645 glfwSetKeyCallback(platform.handle, KeyCallback); 1646 glfwSetCharCallback(platform.handle, CharCallback); 1647 glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); 1648 glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes 1649 glfwSetScrollCallback(platform.handle, MouseScrollCallback); 1650 glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); 1651 glfwSetJoystickCallback(JoystickCallback); 1652 1653 glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) 1654 1655 // Retrieve gamepad names 1656 for (int i = 0; i < MAX_GAMEPADS; i++) 1657 { 1658 if (glfwJoystickPresent(i)) strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); 1659 } 1660 //---------------------------------------------------------------------------- 1661 1662 // Initialize timming system 1663 //---------------------------------------------------------------------------- 1664 InitTimer(); 1665 //---------------------------------------------------------------------------- 1666 1667 // Initialize storage system 1668 //---------------------------------------------------------------------------- 1669 CORE.Storage.basePath = GetWorkingDirectory(); 1670 //---------------------------------------------------------------------------- 1671 1672 #if defined(__NetBSD__) 1673 // Workaround for NetBSD 1674 char *glfwPlatform = "X11"; 1675 #else 1676 char *glfwPlatform = ""; 1677 switch (glfwGetPlatform()) 1678 { 1679 case GLFW_PLATFORM_WIN32: glfwPlatform = "Win32"; break; 1680 case GLFW_PLATFORM_COCOA: glfwPlatform = "Cocoa"; break; 1681 case GLFW_PLATFORM_WAYLAND: glfwPlatform = "Wayland"; break; 1682 case GLFW_PLATFORM_X11: glfwPlatform = "X11"; break; 1683 case GLFW_PLATFORM_NULL: glfwPlatform = "Null"; break; 1684 default: break; 1685 } 1686 #endif 1687 1688 TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW - %s): Initialized successfully", glfwPlatform); 1689 1690 return 0; 1691 } 1692 1693 // Close platform 1694 void ClosePlatform(void) 1695 { 1696 glfwDestroyWindow(platform.handle); 1697 glfwTerminate(); 1698 1699 #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) 1700 timeEndPeriod(1); // Restore time period 1701 #endif 1702 } 1703 1704 // GLFW3 Error Callback, runs on GLFW3 error 1705 static void ErrorCallback(int error, const char *description) 1706 { 1707 TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description); 1708 } 1709 1710 // GLFW3 WindowSize Callback, runs when window is resizedLastFrame 1711 // NOTE: Window resizing not allowed by default 1712 static void WindowSizeCallback(GLFWwindow *window, int width, int height) 1713 { 1714 // Reset viewport and projection matrix for new size 1715 SetupViewport(width, height); 1716 1717 CORE.Window.currentFbo.width = width; 1718 CORE.Window.currentFbo.height = height; 1719 CORE.Window.resizedLastFrame = true; 1720 1721 if (IsWindowFullscreen()) return; 1722 1723 // Set current screen size 1724 1725 CORE.Window.screen.width = width; 1726 CORE.Window.screen.height = height; 1727 1728 // NOTE: Postprocessing texture is not scaled to new size 1729 } 1730 static void WindowPosCallback(GLFWwindow* window, int x, int y) 1731 { 1732 // Set current window position 1733 CORE.Window.position.x = x; 1734 CORE.Window.position.y = y; 1735 } 1736 static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley) 1737 { 1738 CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f); 1739 } 1740 1741 // GLFW3 WindowIconify Callback, runs when window is minimized/restored 1742 static void WindowIconifyCallback(GLFWwindow *window, int iconified) 1743 { 1744 if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified 1745 else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored 1746 } 1747 1748 // GLFW3 WindowMaximize Callback, runs when window is maximized/restored 1749 static void WindowMaximizeCallback(GLFWwindow *window, int maximized) 1750 { 1751 if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized 1752 else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored 1753 } 1754 1755 // GLFW3 WindowFocus Callback, runs when window get/lose focus 1756 static void WindowFocusCallback(GLFWwindow *window, int focused) 1757 { 1758 if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused 1759 else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus 1760 } 1761 1762 // GLFW3 Window Drop Callback, runs when drop files into window 1763 static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) 1764 { 1765 if (count > 0) 1766 { 1767 // In case previous dropped filepaths have not been freed, we free them 1768 if (CORE.Window.dropFileCount > 0) 1769 { 1770 for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); 1771 1772 RL_FREE(CORE.Window.dropFilepaths); 1773 1774 CORE.Window.dropFileCount = 0; 1775 CORE.Window.dropFilepaths = NULL; 1776 } 1777 1778 // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy 1779 CORE.Window.dropFileCount = count; 1780 CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); 1781 1782 for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) 1783 { 1784 CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); 1785 strcpy(CORE.Window.dropFilepaths[i], paths[i]); 1786 } 1787 } 1788 } 1789 1790 // GLFW3 Keyboard Callback, runs on key pressed 1791 static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) 1792 { 1793 if (key < 0) return; // Security check, macOS fn key generates -1 1794 1795 // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 1796 // to work properly with our implementation (IsKeyDown/IsKeyUp checks) 1797 if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; 1798 else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; 1799 else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; 1800 1801 // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys 1802 if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) || 1803 ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1; 1804 1805 // Check if there is space available in the key queue 1806 if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) 1807 { 1808 // Add character to the queue 1809 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; 1810 CORE.Input.Keyboard.keyPressedQueueCount++; 1811 } 1812 1813 // Check the exit key to set close window 1814 if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); 1815 } 1816 1817 // GLFW3 Char Callback, get unicode codepoint value 1818 static void CharCallback(GLFWwindow *window, unsigned int codepoint) 1819 { 1820 //TRACELOG(LOG_DEBUG, "Char Callback: Codepoint: %i", codepoint); 1821 1822 // NOTE: Registers any key down considering OS keyboard layout but 1823 // does not detect action events, those should be managed by user... 1824 // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907 1825 // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char 1826 1827 // Check if there is space available in the queue 1828 if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) 1829 { 1830 // Add character to the queue 1831 CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint; 1832 CORE.Input.Keyboard.charPressedQueueCount++; 1833 } 1834 } 1835 1836 // GLFW3 Mouse Button Callback, runs on mouse button pressed 1837 static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) 1838 { 1839 // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now, 1840 // but future releases may add more actions (i.e. GLFW_REPEAT) 1841 CORE.Input.Mouse.currentButtonState[button] = action; 1842 CORE.Input.Touch.currentTouchState[button] = action; 1843 1844 #if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) 1845 // Process mouse events as touches to be able to use mouse-gestures 1846 GestureEvent gestureEvent = { 0 }; 1847 1848 // Register touch actions 1849 if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN; 1850 else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP; 1851 1852 // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback() 1853 1854 // Assign a pointer ID 1855 gestureEvent.pointId[0] = 0; 1856 1857 // Register touch points count 1858 gestureEvent.pointCount = 1; 1859 1860 // Register touch points position, only one point registered 1861 gestureEvent.position[0] = GetMousePosition(); 1862 1863 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height 1864 gestureEvent.position[0].x /= (float)GetScreenWidth(); 1865 gestureEvent.position[0].y /= (float)GetScreenHeight(); 1866 1867 // Gesture data is sent to gestures-system for processing 1868 ProcessGestureEvent(gestureEvent); 1869 #endif 1870 } 1871 1872 // GLFW3 Cursor Position Callback, runs on mouse move 1873 static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) 1874 { 1875 CORE.Input.Mouse.currentPosition.x = (float)x; 1876 CORE.Input.Mouse.currentPosition.y = (float)y; 1877 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; 1878 1879 #if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) 1880 // Process mouse events as touches to be able to use mouse-gestures 1881 GestureEvent gestureEvent = { 0 }; 1882 1883 gestureEvent.touchAction = TOUCH_ACTION_MOVE; 1884 1885 // Assign a pointer ID 1886 gestureEvent.pointId[0] = 0; 1887 1888 // Register touch points count 1889 gestureEvent.pointCount = 1; 1890 1891 // Register touch points position, only one point registered 1892 gestureEvent.position[0] = CORE.Input.Touch.position[0]; 1893 1894 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height 1895 gestureEvent.position[0].x /= (float)GetScreenWidth(); 1896 gestureEvent.position[0].y /= (float)GetScreenHeight(); 1897 1898 // Gesture data is sent to gestures-system for processing 1899 ProcessGestureEvent(gestureEvent); 1900 #endif 1901 } 1902 1903 // GLFW3 Scrolling Callback, runs on mouse wheel 1904 static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) 1905 { 1906 CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; 1907 } 1908 1909 // GLFW3 CursorEnter Callback, when cursor enters the window 1910 static void CursorEnterCallback(GLFWwindow *window, int enter) 1911 { 1912 if (enter) CORE.Input.Mouse.cursorOnScreen = true; 1913 else CORE.Input.Mouse.cursorOnScreen = false; 1914 } 1915 1916 // GLFW3 Joystick Connected/Disconnected Callback 1917 static void JoystickCallback(int jid, int event) 1918 { 1919 if (event == GLFW_CONNECTED) 1920 { 1921 strcpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid)); 1922 } 1923 else if (event == GLFW_DISCONNECTED) 1924 { 1925 memset(CORE.Input.Gamepad.name[jid], 0, 64); 1926 } 1927 } 1928 1929 #ifdef _WIN32 1930 # define WIN32_CLIPBOARD_IMPLEMENTATION 1931 # include "../external/win32_clipboard.h" 1932 #endif 1933 // EOF