rcore_desktop_sdl.c (75450B)
1 /********************************************************************************************** 2 * 3 * rcore_desktop_sdl - Functions to manage window, graphics device and inputs 4 * 5 * PLATFORM: DESKTOP: SDL 6 * - Windows (Win32, Win64) 7 * - Linux (X11/Wayland desktop mode) 8 * - Others (not tested) 9 * 10 * LIMITATIONS: 11 * - Limitation 01 12 * - Limitation 02 13 * 14 * POSSIBLE IMPROVEMENTS: 15 * - Improvement 01 16 * - Improvement 02 17 * 18 * ADDITIONAL NOTES: 19 * - TRACELOG() function is located in raylib [utils] module 20 * 21 * CONFIGURATION: 22 * #define RCORE_PLATFORM_CUSTOM_FLAG 23 * Custom flag for rcore on target platform -not used- 24 * 25 * DEPENDENCIES: 26 * - SDL 2 or SLD 3 (main library): Windowing and inputs management 27 * - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) 28 * 29 * 30 * LICENSE: zlib/libpng 31 * 32 * Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors 33 * 34 * This software is provided "as-is", without any express or implied warranty. In no event 35 * will the authors be held liable for any damages arising from the use of this software. 36 * 37 * Permission is granted to anyone to use this software for any purpose, including commercial 38 * applications, and to alter it and redistribute it freely, subject to the following restrictions: 39 * 40 * 1. The origin of this software must not be misrepresented; you must not claim that you 41 * wrote the original software. If you use this software in a product, an acknowledgment 42 * in the product documentation would be appreciated but is not required. 43 * 44 * 2. Altered source versions must be plainly marked as such, and must not be misrepresented 45 * as being the original software. 46 * 47 * 3. This notice may not be removed or altered from any source distribution. 48 * 49 **********************************************************************************************/ 50 51 52 #ifndef SDL_ENABLE_OLD_NAMES 53 #define SDL_ENABLE_OLD_NAMES // Just in case we're on SDL3, we need some in-between compatibily 54 #endif 55 #include "SDL.h" // SDL base library (window/rendered, input, timing... functionality) 56 57 #if defined(GRAPHICS_API_OPENGL_ES2) 58 // It seems it does not need to be included to work 59 //#include "SDL_opengles2.h" 60 #else 61 #include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer) 62 #endif 63 64 //---------------------------------------------------------------------------------- 65 // Defines and Macros 66 //---------------------------------------------------------------------------------- 67 #ifndef MAX_CLIPBOARD_BUFFER_LENGTH 68 #define MAX_CLIPBOARD_BUFFER_LENGTH 1024 // Size of the clipboard buffer used on GetClipboardText() 69 #endif 70 71 #if ((defined(SDL_MAJOR_VERSION) && SDL_MAJOR_VERSION == 3) && (defined(SDL_MINOR_VERSION) && SDL_MINOR_VERSION >= 1)) 72 #ifndef PLATFORM_DESKTOP_SDL3 73 #define PLATFORM_DESKTOP_SDL3 74 #endif 75 #endif 76 77 78 //---------------------------------------------------------------------------------- 79 // Types and Structures Definition 80 //---------------------------------------------------------------------------------- 81 typedef struct { 82 SDL_Window *window; 83 SDL_GLContext glContext; 84 85 SDL_GameController *gamepad[MAX_GAMEPADS]; 86 SDL_Cursor *cursor; 87 bool cursorRelative; 88 } PlatformData; 89 90 //---------------------------------------------------------------------------------- 91 // Global Variables Definition 92 //---------------------------------------------------------------------------------- 93 extern CoreData CORE; // Global CORE state context 94 95 static PlatformData platform = { 0 }; // Platform specific data 96 97 //---------------------------------------------------------------------------------- 98 // Local Variables Definition 99 //---------------------------------------------------------------------------------- 100 #define SCANCODE_MAPPED_NUM 232 101 static const KeyboardKey mapScancodeToKey[SCANCODE_MAPPED_NUM] = { 102 KEY_NULL, // SDL_SCANCODE_UNKNOWN 103 0, 104 0, 105 0, 106 KEY_A, // SDL_SCANCODE_A 107 KEY_B, // SDL_SCANCODE_B 108 KEY_C, // SDL_SCANCODE_C 109 KEY_D, // SDL_SCANCODE_D 110 KEY_E, // SDL_SCANCODE_E 111 KEY_F, // SDL_SCANCODE_F 112 KEY_G, // SDL_SCANCODE_G 113 KEY_H, // SDL_SCANCODE_H 114 KEY_I, // SDL_SCANCODE_I 115 KEY_J, // SDL_SCANCODE_J 116 KEY_K, // SDL_SCANCODE_K 117 KEY_L, // SDL_SCANCODE_L 118 KEY_M, // SDL_SCANCODE_M 119 KEY_N, // SDL_SCANCODE_N 120 KEY_O, // SDL_SCANCODE_O 121 KEY_P, // SDL_SCANCODE_P 122 KEY_Q, // SDL_SCANCODE_Q 123 KEY_R, // SDL_SCANCODE_R 124 KEY_S, // SDL_SCANCODE_S 125 KEY_T, // SDL_SCANCODE_T 126 KEY_U, // SDL_SCANCODE_U 127 KEY_V, // SDL_SCANCODE_V 128 KEY_W, // SDL_SCANCODE_W 129 KEY_X, // SDL_SCANCODE_X 130 KEY_Y, // SDL_SCANCODE_Y 131 KEY_Z, // SDL_SCANCODE_Z 132 KEY_ONE, // SDL_SCANCODE_1 133 KEY_TWO, // SDL_SCANCODE_2 134 KEY_THREE, // SDL_SCANCODE_3 135 KEY_FOUR, // SDL_SCANCODE_4 136 KEY_FIVE, // SDL_SCANCODE_5 137 KEY_SIX, // SDL_SCANCODE_6 138 KEY_SEVEN, // SDL_SCANCODE_7 139 KEY_EIGHT, // SDL_SCANCODE_8 140 KEY_NINE, // SDL_SCANCODE_9 141 KEY_ZERO, // SDL_SCANCODE_0 142 KEY_ENTER, // SDL_SCANCODE_RETURN 143 KEY_ESCAPE, // SDL_SCANCODE_ESCAPE 144 KEY_BACKSPACE, // SDL_SCANCODE_BACKSPACE 145 KEY_TAB, // SDL_SCANCODE_TAB 146 KEY_SPACE, // SDL_SCANCODE_SPACE 147 KEY_MINUS, // SDL_SCANCODE_MINUS 148 KEY_EQUAL, // SDL_SCANCODE_EQUALS 149 KEY_LEFT_BRACKET, // SDL_SCANCODE_LEFTBRACKET 150 KEY_RIGHT_BRACKET, // SDL_SCANCODE_RIGHTBRACKET 151 KEY_BACKSLASH, // SDL_SCANCODE_BACKSLASH 152 0, // SDL_SCANCODE_NONUSHASH 153 KEY_SEMICOLON, // SDL_SCANCODE_SEMICOLON 154 KEY_APOSTROPHE, // SDL_SCANCODE_APOSTROPHE 155 KEY_GRAVE, // SDL_SCANCODE_GRAVE 156 KEY_COMMA, // SDL_SCANCODE_COMMA 157 KEY_PERIOD, // SDL_SCANCODE_PERIOD 158 KEY_SLASH, // SDL_SCANCODE_SLASH 159 KEY_CAPS_LOCK, // SDL_SCANCODE_CAPSLOCK 160 KEY_F1, // SDL_SCANCODE_F1 161 KEY_F2, // SDL_SCANCODE_F2 162 KEY_F3, // SDL_SCANCODE_F3 163 KEY_F4, // SDL_SCANCODE_F4 164 KEY_F5, // SDL_SCANCODE_F5 165 KEY_F6, // SDL_SCANCODE_F6 166 KEY_F7, // SDL_SCANCODE_F7 167 KEY_F8, // SDL_SCANCODE_F8 168 KEY_F9, // SDL_SCANCODE_F9 169 KEY_F10, // SDL_SCANCODE_F10 170 KEY_F11, // SDL_SCANCODE_F11 171 KEY_F12, // SDL_SCANCODE_F12 172 KEY_PRINT_SCREEN, // SDL_SCANCODE_PRINTSCREEN 173 KEY_SCROLL_LOCK, // SDL_SCANCODE_SCROLLLOCK 174 KEY_PAUSE, // SDL_SCANCODE_PAUSE 175 KEY_INSERT, // SDL_SCANCODE_INSERT 176 KEY_HOME, // SDL_SCANCODE_HOME 177 KEY_PAGE_UP, // SDL_SCANCODE_PAGEUP 178 KEY_DELETE, // SDL_SCANCODE_DELETE 179 KEY_END, // SDL_SCANCODE_END 180 KEY_PAGE_DOWN, // SDL_SCANCODE_PAGEDOWN 181 KEY_RIGHT, // SDL_SCANCODE_RIGHT 182 KEY_LEFT, // SDL_SCANCODE_LEFT 183 KEY_DOWN, // SDL_SCANCODE_DOWN 184 KEY_UP, // SDL_SCANCODE_UP 185 KEY_NUM_LOCK, // SDL_SCANCODE_NUMLOCKCLEAR 186 KEY_KP_DIVIDE, // SDL_SCANCODE_KP_DIVIDE 187 KEY_KP_MULTIPLY, // SDL_SCANCODE_KP_MULTIPLY 188 KEY_KP_SUBTRACT, // SDL_SCANCODE_KP_MINUS 189 KEY_KP_ADD, // SDL_SCANCODE_KP_PLUS 190 KEY_KP_ENTER, // SDL_SCANCODE_KP_ENTER 191 KEY_KP_1, // SDL_SCANCODE_KP_1 192 KEY_KP_2, // SDL_SCANCODE_KP_2 193 KEY_KP_3, // SDL_SCANCODE_KP_3 194 KEY_KP_4, // SDL_SCANCODE_KP_4 195 KEY_KP_5, // SDL_SCANCODE_KP_5 196 KEY_KP_6, // SDL_SCANCODE_KP_6 197 KEY_KP_7, // SDL_SCANCODE_KP_7 198 KEY_KP_8, // SDL_SCANCODE_KP_8 199 KEY_KP_9, // SDL_SCANCODE_KP_9 200 KEY_KP_0, // SDL_SCANCODE_KP_0 201 KEY_KP_DECIMAL, // SDL_SCANCODE_KP_PERIOD 202 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 204 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 205 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 207 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 209 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 211 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 213 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 214 0, 0, 0, 0, 215 KEY_LEFT_CONTROL, //SDL_SCANCODE_LCTRL 216 KEY_LEFT_SHIFT, //SDL_SCANCODE_LSHIFT 217 KEY_LEFT_ALT, //SDL_SCANCODE_LALT 218 KEY_LEFT_SUPER, //SDL_SCANCODE_LGUI 219 KEY_RIGHT_CONTROL, //SDL_SCANCODE_RCTRL 220 KEY_RIGHT_SHIFT, //SDL_SCANCODE_RSHIFT 221 KEY_RIGHT_ALT, //SDL_SCANCODE_RALT 222 KEY_RIGHT_SUPER //SDL_SCANCODE_RGUI 223 }; 224 225 static const int CursorsLUT[] = { 226 SDL_SYSTEM_CURSOR_ARROW, // 0 MOUSE_CURSOR_DEFAULT 227 SDL_SYSTEM_CURSOR_ARROW, // 1 MOUSE_CURSOR_ARROW 228 SDL_SYSTEM_CURSOR_IBEAM, // 2 MOUSE_CURSOR_IBEAM 229 SDL_SYSTEM_CURSOR_CROSSHAIR, // 3 MOUSE_CURSOR_CROSSHAIR 230 SDL_SYSTEM_CURSOR_HAND, // 4 MOUSE_CURSOR_POINTING_HAND 231 SDL_SYSTEM_CURSOR_SIZEWE, // 5 MOUSE_CURSOR_RESIZE_EW 232 SDL_SYSTEM_CURSOR_SIZENS, // 6 MOUSE_CURSOR_RESIZE_NS 233 SDL_SYSTEM_CURSOR_SIZENWSE, // 7 MOUSE_CURSOR_RESIZE_NWSE 234 SDL_SYSTEM_CURSOR_SIZENESW, // 8 MOUSE_CURSOR_RESIZE_NESW 235 SDL_SYSTEM_CURSOR_SIZEALL, // 9 MOUSE_CURSOR_RESIZE_ALL 236 SDL_SYSTEM_CURSOR_NO // 10 MOUSE_CURSOR_NOT_ALLOWED 237 //SDL_SYSTEM_CURSOR_WAIT, // No equivalent implemented on MouseCursor enum on raylib.h 238 //SDL_SYSTEM_CURSOR_WAITARROW, // No equivalent implemented on MouseCursor enum on raylib.h 239 }; 240 241 242 // SDL3 Migration Layer made to avoid `ifdefs` inside functions when we can. 243 #ifdef PLATFORM_DESKTOP_SDL3 244 245 // SDL3 Migration: 246 // SDL_WINDOW_FULLSCREEN_DESKTOP has been removed, 247 // and you can call SDL_GetWindowFullscreenMode() 248 // to see whether an exclusive fullscreen mode will be used 249 // or the borderless fullscreen desktop mode will be used 250 #define SDL_WINDOW_FULLSCREEN_DESKTOP SDL_WINDOW_FULLSCREEN 251 252 253 #define SDL_IGNORE false 254 #define SDL_DISABLE false 255 #define SDL_ENABLE true 256 257 // SDL3 Migration: SDL_INIT_TIMER - no longer needed before calling SDL_AddTimer() 258 #define SDL_INIT_TIMER 0x0 // It's a flag, so no problem in setting it to zero if we use in a bitor (|) 259 260 // SDL3 Migration: The SDL_WINDOW_SHOWN flag has been removed. Windows are shown by default and can be created hidden by using the SDL_WINDOW_HIDDEN flag. 261 #define SDL_WINDOW_SHOWN 0x0 // It's a flag, so no problem in setting it to zero if we use in a bitor (|) 262 263 // 264 // SDL3 Migration: Renamed 265 // IMPORTANT: 266 // Might need to call SDL_CleanupEvent somewhere see :https://github.com/libsdl-org/SDL/issues/3540#issuecomment-1793449852 267 // 268 #define SDL_DROPFILE SDL_EVENT_DROP_FILE 269 270 271 const char* SDL_GameControllerNameForIndex(int joystickIndex) 272 { 273 // NOTE: SDL3 uses the IDs itself (SDL_JoystickID) instead of SDL2 joystick_index 274 const char* name = NULL; 275 int numJoysticks = 0; 276 SDL_JoystickID *joysticks = SDL_GetJoysticks(&numJoysticks); 277 if (joysticks) { 278 if (joystickIndex < numJoysticks) { 279 SDL_JoystickID instance_id = joysticks[joystickIndex]; 280 name = SDL_GetGamepadNameForID(instance_id); 281 } 282 SDL_free(joysticks); 283 } 284 return name; 285 } 286 287 int SDL_GetNumVideoDisplays(void) 288 { 289 int monitorCount = 0; 290 SDL_DisplayID *displays = SDL_GetDisplays(&monitorCount); 291 // Safe because If `mem` is NULL, SDL_free does nothing. 292 SDL_free(displays); 293 294 return monitorCount; 295 } 296 297 298 // SLD3 Migration: 299 // To emulate SDL2 this function should return `SDL_DISABLE` or `SDL_ENABLE` 300 // representing the *processing state* of the event before this function makes any changes to it. 301 Uint8 SDL_EventState(Uint32 type, int state) { 302 303 Uint8 stateBefore = SDL_EventEnabled(type); 304 switch (state) 305 { 306 case SDL_DISABLE: SDL_SetEventEnabled(type, false); break; 307 case SDL_ENABLE: SDL_SetEventEnabled(type, true); break; 308 default: TRACELOG(LOG_WARNING, "Event sate: unknow type"); 309 } 310 return stateBefore; 311 } 312 313 void SDL_GetCurrentDisplayMode_Adapter(SDL_DisplayID displayID, SDL_DisplayMode* mode) 314 { 315 const SDL_DisplayMode* currMode = SDL_GetCurrentDisplayMode(displayID); 316 if (currMode == NULL) 317 { 318 TRACELOG(LOG_WARNING, "No current display mode"); 319 } 320 else 321 { 322 *mode = *currMode; 323 } 324 } 325 326 // SDL3 Migration: Renamed 327 #define SDL_GetCurrentDisplayMode SDL_GetCurrentDisplayMode_Adapter 328 329 330 SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) 331 { 332 return SDL_CreateSurface(width, height, SDL_GetPixelFormatForMasks(depth, Rmask, Gmask, Bmask, Amask)); 333 } 334 335 // SDL3 Migration: 336 // SDL_GetDisplayDPI() - 337 // not reliable across platforms, approximately replaced by multiplying 338 // SDL_GetWindowDisplayScale() times 160 on iPhone and Android, and 96 on other platforms. 339 // returns 0 on success or a negative error code on failure 340 int SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi) { 341 float dpi = SDL_GetWindowDisplayScale(platform.window) * 96.0; 342 if (ddpi != NULL) *ddpi = dpi; 343 if (hdpi != NULL) *hdpi = dpi; 344 if (vdpi != NULL) *vdpi = dpi; 345 return 0; 346 } 347 348 SDL_Surface *SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, Uint32 format) 349 { 350 return SDL_CreateSurface(width, height, format); 351 } 352 353 SDL_Surface *SDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) 354 { 355 return SDL_CreateSurfaceFrom(width, height, SDL_GetPixelFormatForMasks(depth, Rmask, Gmask, Bmask, Amask), pixels, pitch); 356 } 357 358 SDL_Surface *SDL_CreateRGBSurfaceWithFormatFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 format) 359 { 360 return SDL_CreateSurfaceFrom(width, height, format, pixels, pitch); 361 } 362 363 int SDL_NumJoysticks(void) 364 { 365 int numJoysticks; 366 SDL_JoystickID *joysticks = SDL_GetJoysticks(&numJoysticks); 367 SDL_free(joysticks); 368 return numJoysticks; 369 } 370 371 372 // SDL_SetRelativeMouseMode 373 // returns 0 on success or a negative error code on failure 374 // If relative mode is not supported, this returns -1. 375 int SDL_SetRelativeMouseMode_Adapter(SDL_bool enabled) 376 { 377 378 // 379 // SDL_SetWindowRelativeMouseMode(SDL_Window *window, bool enabled) 380 // \returns true on success or false on failure; call SDL_GetError() for more 381 // 382 if (SDL_SetWindowRelativeMouseMode(platform.window, enabled)) 383 { 384 return 0; // success 385 } 386 else 387 { 388 return -1; // failure 389 } 390 } 391 392 #define SDL_SetRelativeMouseMode SDL_SetRelativeMouseMode_Adapter 393 394 bool SDL_GetRelativeMouseMode_Adapter(void) 395 { 396 return SDL_GetWindowRelativeMouseMode(platform.window); 397 } 398 399 #define SDL_GetRelativeMouseMode SDL_GetRelativeMouseMode_Adapter 400 401 402 int SDL_GetNumTouchFingers(SDL_TouchID touchID) 403 { 404 // SDL_Finger **SDL_GetTouchFingers(SDL_TouchID touchID, int *count) 405 int count = 0; 406 SDL_Finger **fingers = SDL_GetTouchFingers(touchID, &count); 407 SDL_free(fingers); 408 return count; 409 } 410 411 #else // We're on SDL2 412 413 // Since SDL2 doesn't have this function we leave a stub 414 // SDL_GetClipboardData function is available since SDL 3.1.3. (e.g. SDL3) 415 void* SDL_GetClipboardData(const char *mime_type, size_t *size) { 416 TRACELOG(LOG_WARNING, "Getting clipboard data that is not text is only available in SDL3"); 417 // We could possibly implement it ourselves in this case for some easier platforms 418 return NULL; 419 } 420 421 #endif // PLATFORM_DESKTOP_SDL3 422 423 424 425 //---------------------------------------------------------------------------------- 426 // Module Internal Functions Declaration 427 //---------------------------------------------------------------------------------- 428 int InitPlatform(void); // Initialize platform (graphics, inputs and more) 429 void ClosePlatform(void); // Close platform 430 431 static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help convert SDL scancodes to raylib key 432 433 //---------------------------------------------------------------------------------- 434 // Module Functions Declaration 435 //---------------------------------------------------------------------------------- 436 // NOTE: Functions declaration is provided by raylib.h 437 438 //---------------------------------------------------------------------------------- 439 // Module Functions Definition: Window and Graphics Device 440 //---------------------------------------------------------------------------------- 441 442 // Check if application should close 443 bool WindowShouldClose(void) 444 { 445 if (CORE.Window.ready) return CORE.Window.shouldClose; 446 else return true; 447 } 448 449 // Toggle fullscreen mode 450 void ToggleFullscreen(void) 451 { 452 const int monitor = SDL_GetWindowDisplayIndex(platform.window); 453 const int monitorCount = SDL_GetNumVideoDisplays(); 454 455 #ifdef PLATFORM_DESKTOP_SDL3 // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure 456 if ((monitor > 0) && (monitor <= monitorCount)) 457 #else 458 if ((monitor >= 0) && (monitor < monitorCount)) 459 #endif 460 { 461 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) 462 { 463 SDL_SetWindowFullscreen(platform.window, 0); 464 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; 465 CORE.Window.fullscreen = false; 466 } 467 else 468 { 469 SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); 470 CORE.Window.flags |= FLAG_FULLSCREEN_MODE; 471 CORE.Window.fullscreen = true; 472 } 473 } 474 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 475 } 476 477 // Toggle borderless windowed mode 478 void ToggleBorderlessWindowed(void) 479 { 480 const int monitor = SDL_GetWindowDisplayIndex(platform.window); 481 const int monitorCount = SDL_GetNumVideoDisplays(); 482 #ifdef PLATFORM_DESKTOP_SDL3 // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure 483 if ((monitor > 0) && (monitor <= monitorCount)) 484 #else 485 if ((monitor >= 0) && (monitor < monitorCount)) 486 #endif 487 { 488 if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) 489 { 490 SDL_SetWindowFullscreen(platform.window, 0); 491 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; 492 } 493 else 494 { 495 SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP); 496 CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; 497 } 498 } 499 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 500 } 501 502 // Set window state: maximized, if resizable 503 void MaximizeWindow(void) 504 { 505 SDL_MaximizeWindow(platform.window); 506 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; 507 } 508 509 // Set window state: minimized 510 void MinimizeWindow(void) 511 { 512 SDL_MinimizeWindow(platform.window); 513 CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; 514 } 515 516 // Set window state: not minimized/maximized 517 void RestoreWindow(void) 518 { 519 SDL_ShowWindow(platform.window); 520 } 521 522 // Set window configuration state using flags 523 void SetWindowState(unsigned int flags) 524 { 525 CORE.Window.flags |= flags; 526 527 if (flags & FLAG_VSYNC_HINT) 528 { 529 SDL_GL_SetSwapInterval(1); 530 } 531 if (flags & FLAG_FULLSCREEN_MODE) 532 { 533 const int monitor = SDL_GetWindowDisplayIndex(platform.window); 534 const int monitorCount = SDL_GetNumVideoDisplays(); 535 #ifdef PLATFORM_DESKTOP_SDL3 // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure 536 if ((monitor > 0) && (monitor <= monitorCount)) 537 #else 538 if ((monitor >= 0) && (monitor < monitorCount)) 539 #endif 540 { 541 SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); 542 CORE.Window.fullscreen = true; 543 } 544 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 545 } 546 if (flags & FLAG_WINDOW_RESIZABLE) 547 { 548 SDL_SetWindowResizable(platform.window, SDL_TRUE); 549 } 550 if (flags & FLAG_WINDOW_UNDECORATED) 551 { 552 SDL_SetWindowBordered(platform.window, SDL_FALSE); 553 } 554 if (flags & FLAG_WINDOW_HIDDEN) 555 { 556 SDL_HideWindow(platform.window); 557 } 558 if (flags & FLAG_WINDOW_MINIMIZED) 559 { 560 SDL_MinimizeWindow(platform.window); 561 } 562 if (flags & FLAG_WINDOW_MAXIMIZED) 563 { 564 SDL_MaximizeWindow(platform.window); 565 } 566 if (flags & FLAG_WINDOW_UNFOCUSED) 567 { 568 // NOTE: To be able to implement this part it seems that we should 569 // do it ourselves, via `Windows.h`, `X11/Xlib.h` or even `Cocoa.h` 570 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); 571 } 572 if (flags & FLAG_WINDOW_TOPMOST) 573 { 574 SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); 575 } 576 if (flags & FLAG_WINDOW_ALWAYS_RUN) 577 { 578 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL"); 579 } 580 if (flags & FLAG_WINDOW_TRANSPARENT) 581 { 582 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL"); 583 } 584 if (flags & FLAG_WINDOW_HIGHDPI) 585 { 586 // NOTE: Such a function does not seem to exist 587 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL"); 588 } 589 if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) 590 { 591 //SDL_SetWindowGrab(platform.window, SDL_FALSE); 592 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL"); 593 } 594 if (flags & FLAG_BORDERLESS_WINDOWED_MODE) 595 { 596 const int monitor = SDL_GetWindowDisplayIndex(platform.window); 597 const int monitorCount = SDL_GetNumVideoDisplays(); 598 #ifdef PLATFORM_DESKTOP_SDL3 // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure 599 if ((monitor > 0) && (monitor <= monitorCount)) 600 #else 601 if ((monitor >= 0) && (monitor < monitorCount)) 602 #endif 603 { 604 SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP); 605 } 606 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 607 } 608 if (flags & FLAG_MSAA_4X_HINT) 609 { 610 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable multisampling buffers 611 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); // Enable multisampling 612 } 613 if (flags & FLAG_INTERLACED_HINT) 614 { 615 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL"); 616 } 617 } 618 619 // Clear window configuration state flags 620 void ClearWindowState(unsigned int flags) 621 { 622 CORE.Window.flags &= ~flags; 623 624 if (flags & FLAG_VSYNC_HINT) 625 { 626 SDL_GL_SetSwapInterval(0); 627 } 628 if (flags & FLAG_FULLSCREEN_MODE) 629 { 630 SDL_SetWindowFullscreen(platform.window, 0); 631 CORE.Window.fullscreen = false; 632 } 633 if (flags & FLAG_WINDOW_RESIZABLE) 634 { 635 SDL_SetWindowResizable(platform.window, SDL_FALSE); 636 } 637 if (flags & FLAG_WINDOW_UNDECORATED) 638 { 639 SDL_SetWindowBordered(platform.window, SDL_TRUE); 640 } 641 if (flags & FLAG_WINDOW_HIDDEN) 642 { 643 SDL_ShowWindow(platform.window); 644 } 645 if (flags & FLAG_WINDOW_MINIMIZED) 646 { 647 SDL_RestoreWindow(platform.window); 648 } 649 if (flags & FLAG_WINDOW_MAXIMIZED) 650 { 651 SDL_RestoreWindow(platform.window); 652 } 653 if (flags & FLAG_WINDOW_UNFOCUSED) 654 { 655 //SDL_RaiseWindow(platform.window); 656 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); 657 } 658 if (flags & FLAG_WINDOW_TOPMOST) 659 { 660 SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); 661 } 662 if (flags & FLAG_WINDOW_ALWAYS_RUN) 663 { 664 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL"); 665 } 666 if (flags & FLAG_WINDOW_TRANSPARENT) 667 { 668 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL"); 669 } 670 if (flags & FLAG_WINDOW_HIGHDPI) 671 { 672 // NOTE: There also doesn't seem to be a feature to disable high DPI once enabled 673 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL"); 674 } 675 if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) 676 { 677 //SDL_SetWindowGrab(platform.window, SDL_TRUE); 678 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL"); 679 } 680 if (flags & FLAG_BORDERLESS_WINDOWED_MODE) 681 { 682 SDL_SetWindowFullscreen(platform.window, 0); 683 } 684 if (flags & FLAG_MSAA_4X_HINT) 685 { 686 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); // Disable multisampling buffers 687 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); // Disable multisampling 688 } 689 if (flags & FLAG_INTERLACED_HINT) 690 { 691 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL"); 692 } 693 } 694 695 // Set icon for window 696 void SetWindowIcon(Image image) 697 { 698 SDL_Surface *iconSurface = NULL; 699 700 unsigned int rmask = 0, gmask = 0, bmask = 0, amask = 0; 701 int depth = 0; // Depth in bits 702 int pitch = 0; // Pixel spacing (pitch) in bytes 703 704 switch (image.format) 705 { 706 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: 707 rmask = 0xFF, gmask = 0; 708 bmask = 0, amask = 0; 709 depth = 8, pitch = image.width; 710 break; 711 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: 712 rmask = 0xFF, gmask = 0xFF00; 713 bmask = 0, amask = 0; 714 depth = 16, pitch = image.width*2; 715 break; 716 case PIXELFORMAT_UNCOMPRESSED_R5G6B5: 717 rmask = 0xF800, gmask = 0x07E0; 718 bmask = 0x001F, amask = 0; 719 depth = 16, pitch = image.width*2; 720 break; 721 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // Uses BGR for 24-bit 722 rmask = 0x0000FF, gmask = 0x00FF00; 723 bmask = 0xFF0000, amask = 0; 724 depth = 24, pitch = image.width*3; 725 break; 726 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: 727 rmask = 0xF800, gmask = 0x07C0; 728 bmask = 0x003E, amask = 0x0001; 729 depth = 16, pitch = image.width*2; 730 break; 731 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: 732 rmask = 0xF000, gmask = 0x0F00; 733 bmask = 0x00F0, amask = 0x000F; 734 depth = 16, pitch = image.width*2; 735 break; 736 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: 737 rmask = 0xFF000000, gmask = 0x00FF0000; 738 bmask = 0x0000FF00, amask = 0x000000FF; 739 depth = 32, pitch = image.width*4; 740 break; 741 case PIXELFORMAT_UNCOMPRESSED_R32: 742 rmask = 0xFFFFFFFF, gmask = 0; 743 bmask = 0, amask = 0; 744 depth = 32, pitch = image.width*4; 745 break; 746 case PIXELFORMAT_UNCOMPRESSED_R32G32B32: 747 rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; 748 bmask = 0xFFFFFFFF, amask = 0; 749 depth = 96, pitch = image.width*12; 750 break; 751 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: 752 rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; 753 bmask = 0xFFFFFFFF, amask = 0xFFFFFFFF; 754 depth = 128, pitch = image.width*16; 755 break; 756 case PIXELFORMAT_UNCOMPRESSED_R16: 757 rmask = 0xFFFF, gmask = 0; 758 bmask = 0, amask = 0; 759 depth = 16, pitch = image.width*2; 760 break; 761 case PIXELFORMAT_UNCOMPRESSED_R16G16B16: 762 rmask = 0xFFFF, gmask = 0xFFFF; 763 bmask = 0xFFFF, amask = 0; 764 depth = 48, pitch = image.width*6; 765 break; 766 case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: 767 rmask = 0xFFFF, gmask = 0xFFFF; 768 bmask = 0xFFFF, amask = 0xFFFF; 769 depth = 64, pitch = image.width*8; 770 break; 771 default: return; // Compressed formats are not supported 772 } 773 774 iconSurface = SDL_CreateRGBSurfaceFrom( image.data, image.width, image.height, depth, pitch, rmask, gmask, bmask, amask ); 775 776 if (iconSurface) 777 { 778 SDL_SetWindowIcon(platform.window, iconSurface); 779 SDL_FreeSurface(iconSurface); 780 } 781 } 782 783 // Set icon for window 784 void SetWindowIcons(Image *images, int count) 785 { 786 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); 787 } 788 789 // Set title for window 790 void SetWindowTitle(const char *title) 791 { 792 SDL_SetWindowTitle(platform.window, title); 793 794 CORE.Window.title = title; 795 } 796 797 // Set window position on screen (windowed mode) 798 void SetWindowPosition(int x, int y) 799 { 800 SDL_SetWindowPosition(platform.window, x, y); 801 802 CORE.Window.position.x = x; 803 CORE.Window.position.y = y; 804 } 805 806 // Set monitor for the current window 807 void SetWindowMonitor(int monitor) 808 { 809 const int monitorCount = SDL_GetNumVideoDisplays(); 810 if ((monitor >= 0) && (monitor < monitorCount)) 811 { 812 // NOTE: 813 // 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3, 814 // see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba 815 // 2. A workaround for SDL2 is leaving fullscreen, moving the window, then entering full screen again. 816 const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)? true : false; 817 818 const int screenWidth = CORE.Window.screen.width; 819 const int screenHeight = CORE.Window.screen.height; 820 SDL_Rect usableBounds; 821 #ifdef PLATFORM_DESKTOP_SDL3 // Different style for success checking 822 if (SDL_GetDisplayUsableBounds(monitor, &usableBounds)) 823 #else 824 if (SDL_GetDisplayUsableBounds(monitor, &usableBounds) == 0) 825 #endif 826 { 827 if (wasFullscreen == 1) ToggleFullscreen(); // Leave fullscreen. 828 829 // If the screen size is larger than the monitor usable area, anchor it on the top left corner, otherwise, center it 830 if ((screenWidth >= usableBounds.w) || (screenHeight >= usableBounds.h)) 831 { 832 // NOTE: 833 // 1. There's a known issue where if the window larger than the target display bounds, 834 // when moving the windows to that display, the window could be clipped back 835 // ending up positioned partly outside the target display. 836 // 2. The workaround for that is, previously to moving the window, 837 // setting the window size to the target display size, so they match. 838 // 3. It wasn't done here because we can't assume changing the window size automatically 839 // is acceptable behavior by the user. 840 SDL_SetWindowPosition(platform.window, usableBounds.x, usableBounds.y); 841 CORE.Window.position.x = usableBounds.x; 842 CORE.Window.position.y = usableBounds.y; 843 } 844 else 845 { 846 const int x = usableBounds.x + (usableBounds.w/2) - (screenWidth/2); 847 const int y = usableBounds.y + (usableBounds.h/2) - (screenHeight/2); 848 SDL_SetWindowPosition(platform.window, x, y); 849 CORE.Window.position.x = x; 850 CORE.Window.position.y = y; 851 } 852 853 if (wasFullscreen == 1) ToggleFullscreen(); // Re-enter fullscreen 854 } 855 else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds"); 856 } 857 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 858 } 859 860 // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) 861 void SetWindowMinSize(int width, int height) 862 { 863 SDL_SetWindowMinimumSize(platform.window, width, height); 864 865 CORE.Window.screenMin.width = width; 866 CORE.Window.screenMin.height = height; 867 } 868 869 // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) 870 void SetWindowMaxSize(int width, int height) 871 { 872 SDL_SetWindowMaximumSize(platform.window, width, height); 873 874 CORE.Window.screenMax.width = width; 875 CORE.Window.screenMax.height = height; 876 } 877 878 // Set window dimensions 879 void SetWindowSize(int width, int height) 880 { 881 SDL_SetWindowSize(platform.window, width, height); 882 883 CORE.Window.screen.width = width; 884 CORE.Window.screen.height = height; 885 } 886 887 // Set window opacity, value opacity is between 0.0 and 1.0 888 void SetWindowOpacity(float opacity) 889 { 890 if (opacity >= 1.0f) opacity = 1.0f; 891 else if (opacity <= 0.0f) opacity = 0.0f; 892 893 SDL_SetWindowOpacity(platform.window, opacity); 894 } 895 896 // Set window focused 897 void SetWindowFocused(void) 898 { 899 SDL_RaiseWindow(platform.window); 900 } 901 902 // Get native window handle 903 void *GetWindowHandle(void) 904 { 905 return (void *)platform.window; 906 } 907 908 // Get number of monitors 909 int GetMonitorCount(void) 910 { 911 int monitorCount = 0; 912 913 monitorCount = SDL_GetNumVideoDisplays(); 914 915 return monitorCount; 916 } 917 918 // Get number of monitors 919 int GetCurrentMonitor(void) 920 { 921 int currentMonitor = 0; 922 923 // Be aware that this returns an ID in SDL3 and a Index in SDL2 924 currentMonitor = SDL_GetWindowDisplayIndex(platform.window); 925 926 return currentMonitor; 927 } 928 929 // Get selected monitor position 930 Vector2 GetMonitorPosition(int monitor) 931 { 932 const int monitorCount = SDL_GetNumVideoDisplays(); 933 if ((monitor >= 0) && (monitor < monitorCount)) 934 { 935 SDL_Rect displayBounds; 936 #ifdef PLATFORM_DESKTOP_SDL3 937 if (SDL_GetDisplayUsableBounds(monitor, &displayBounds)) 938 #else 939 if (SDL_GetDisplayUsableBounds(monitor, &displayBounds) == 0) 940 #endif 941 { 942 return (Vector2){ (float)displayBounds.x, (float)displayBounds.y }; 943 } 944 else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds"); 945 } 946 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 947 return (Vector2){ 0.0f, 0.0f }; 948 } 949 950 // Get selected monitor width (currently used by monitor) 951 int GetMonitorWidth(int monitor) 952 { 953 int width = 0; 954 955 const int monitorCount = SDL_GetNumVideoDisplays(); 956 if ((monitor >= 0) && (monitor < monitorCount)) 957 { 958 SDL_DisplayMode mode; 959 SDL_GetCurrentDisplayMode(monitor, &mode); 960 width = mode.w; 961 } 962 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 963 964 return width; 965 } 966 967 // Get selected monitor height (currently used by monitor) 968 int GetMonitorHeight(int monitor) 969 { 970 int height = 0; 971 972 const int monitorCount = SDL_GetNumVideoDisplays(); 973 if ((monitor >= 0) && (monitor < monitorCount)) 974 { 975 SDL_DisplayMode mode; 976 SDL_GetCurrentDisplayMode(monitor, &mode); 977 height = mode.h; 978 } 979 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 980 981 return height; 982 } 983 984 // Get selected monitor physical width in millimetres 985 int GetMonitorPhysicalWidth(int monitor) 986 { 987 int width = 0; 988 989 const int monitorCount = SDL_GetNumVideoDisplays(); 990 if ((monitor >= 0) && (monitor < monitorCount)) 991 { 992 float ddpi = 0.0f; 993 SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL); 994 SDL_DisplayMode mode; 995 SDL_GetCurrentDisplayMode(monitor, &mode); 996 // Calculate size on inches, then convert to millimeter 997 if (ddpi > 0.0f) width = (mode.w/ddpi)*25.4f; 998 } 999 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 1000 1001 return width; 1002 } 1003 1004 // Get selected monitor physical height in millimetres 1005 int GetMonitorPhysicalHeight(int monitor) 1006 { 1007 int height = 0; 1008 1009 const int monitorCount = SDL_GetNumVideoDisplays(); 1010 if ((monitor >= 0) && (monitor < monitorCount)) 1011 { 1012 float ddpi = 0.0f; 1013 SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL); 1014 SDL_DisplayMode mode; 1015 SDL_GetCurrentDisplayMode(monitor, &mode); 1016 // Calculate size on inches, then convert to millimeter 1017 if (ddpi > 0.0f) height = (mode.h/ddpi)*25.4f; 1018 } 1019 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 1020 1021 return height; 1022 } 1023 1024 // Get selected monitor refresh rate 1025 int GetMonitorRefreshRate(int monitor) 1026 { 1027 int refresh = 0; 1028 1029 const int monitorCount = SDL_GetNumVideoDisplays(); 1030 if ((monitor >= 0) && (monitor < monitorCount)) 1031 { 1032 SDL_DisplayMode mode; 1033 SDL_GetCurrentDisplayMode(monitor, &mode); 1034 refresh = mode.refresh_rate; 1035 } 1036 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 1037 1038 return refresh; 1039 } 1040 1041 // Get the human-readable, UTF-8 encoded name of the selected monitor 1042 const char *GetMonitorName(int monitor) 1043 { 1044 const int monitorCount = SDL_GetNumVideoDisplays(); 1045 1046 if ((monitor >= 0) && (monitor < monitorCount)) return SDL_GetDisplayName(monitor); 1047 else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); 1048 1049 return ""; 1050 } 1051 1052 // Get window position XY on monitor 1053 Vector2 GetWindowPosition(void) 1054 { 1055 int x = 0; 1056 int y = 0; 1057 1058 SDL_GetWindowPosition(platform.window, &x, &y); 1059 1060 return (Vector2){ (float)x, (float)y }; 1061 } 1062 1063 // Get window scale DPI factor for current monitor 1064 Vector2 GetWindowScaleDPI(void) 1065 { 1066 Vector2 scale = { 1.0f, 1.0f }; 1067 1068 #ifndef PLATFORM_DESKTOP_SDL3 1069 // NOTE: SDL_GetWindowDisplayScale was only added on SDL3 1070 // see https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale 1071 // TODO: Implement the window scale factor calculation manually. 1072 TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); 1073 #else 1074 scale.x = SDL_GetWindowDisplayScale(platform.window); 1075 scale.y = scale.x; 1076 TRACELOG(LOG_INFO, "WindowScaleDPI is %f", scale.x); 1077 #endif 1078 1079 return scale; 1080 } 1081 1082 // Set clipboard text content 1083 void SetClipboardText(const char *text) 1084 { 1085 SDL_SetClipboardText(text); 1086 } 1087 1088 // Get clipboard text content 1089 const char *GetClipboardText(void) 1090 { 1091 static char buffer[MAX_CLIPBOARD_BUFFER_LENGTH] = { 0 }; 1092 1093 char *clipboard = SDL_GetClipboardText(); 1094 1095 int clipboardSize = snprintf(buffer, sizeof(buffer), "%s", clipboard); 1096 if (clipboardSize >= MAX_CLIPBOARD_BUFFER_LENGTH) 1097 { 1098 char *truncate = buffer + MAX_CLIPBOARD_BUFFER_LENGTH - 4; 1099 sprintf(truncate, "..."); 1100 } 1101 1102 SDL_free(clipboard); 1103 1104 return buffer; 1105 } 1106 1107 1108 #if defined(SUPPORT_CLIPBOARD_IMAGE) 1109 // Get clipboard image 1110 Image GetClipboardImage(void) 1111 { 1112 // Let's hope compiler put these arrays in static memory 1113 const char *image_formats[] = { 1114 "image/bmp", 1115 "image/png", 1116 "image/jpg", 1117 "image/tiff", 1118 }; 1119 const char *image_extensions[] = { 1120 ".bmp", 1121 ".png", 1122 ".jpg", 1123 ".tiff", 1124 }; 1125 1126 1127 Image image = {0}; 1128 size_t dataSize = 0; 1129 void *fileData = NULL; 1130 for (int i = 0; i < SDL_arraysize(image_formats); ++i) 1131 { 1132 // NOTE: This pointer should be free with SDL_free() at some point. 1133 fileData = SDL_GetClipboardData(image_formats[i], &dataSize); 1134 if (fileData) { 1135 image = LoadImageFromMemory(image_extensions[i], fileData, dataSize); 1136 if (IsImageValid(image)) 1137 { 1138 TRACELOG(LOG_INFO, "Clipboard image: Got image from clipboard as a `%s` successfully", image_extensions[i]); 1139 return image; 1140 } 1141 } 1142 } 1143 1144 TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data. %s", SDL_GetError()); 1145 return image; 1146 } 1147 #endif 1148 1149 1150 // Show mouse cursor 1151 void ShowCursor(void) 1152 { 1153 #ifdef PLATFORM_DESKTOP_SDL3 1154 SDL_ShowCursor(); 1155 #else 1156 SDL_ShowCursor(SDL_ENABLE); 1157 #endif 1158 CORE.Input.Mouse.cursorHidden = false; 1159 } 1160 1161 // Hides mouse cursor 1162 void HideCursor(void) 1163 { 1164 #ifdef PLATFORM_DESKTOP_SDL3 1165 SDL_HideCursor(); 1166 #else 1167 SDL_ShowCursor(SDL_DISABLE); 1168 #endif 1169 CORE.Input.Mouse.cursorHidden = true; 1170 } 1171 1172 // Enables cursor (unlock cursor) 1173 void EnableCursor(void) 1174 { 1175 SDL_SetRelativeMouseMode(SDL_FALSE); 1176 1177 #ifdef PLATFORM_DESKTOP_SDL3 1178 // SDL_ShowCursor() has been split into three functions: SDL_ShowCursor(), SDL_HideCursor(), and SDL_CursorVisible() 1179 SDL_ShowCursor(); 1180 #else 1181 SDL_ShowCursor(SDL_ENABLE); 1182 #endif 1183 1184 platform.cursorRelative = false; 1185 CORE.Input.Mouse.cursorHidden = false; 1186 } 1187 1188 // Disables cursor (lock cursor) 1189 void DisableCursor(void) 1190 { 1191 SDL_SetRelativeMouseMode(SDL_TRUE); 1192 1193 platform.cursorRelative = true; 1194 CORE.Input.Mouse.cursorHidden = true; 1195 } 1196 1197 // Swap back buffer with front buffer (screen drawing) 1198 void SwapScreenBuffer(void) 1199 { 1200 SDL_GL_SwapWindow(platform.window); 1201 } 1202 1203 //---------------------------------------------------------------------------------- 1204 // Module Functions Definition: Misc 1205 //---------------------------------------------------------------------------------- 1206 1207 // Get elapsed time measure in seconds 1208 double GetTime(void) 1209 { 1210 unsigned int ms = SDL_GetTicks(); // Elapsed time in milliseconds since SDL_Init() 1211 double time = (double)ms/1000; 1212 return time; 1213 } 1214 1215 // Open URL with default system browser (if available) 1216 // NOTE: This function is only safe to use if you control the URL given. 1217 // A user could craft a malicious string performing another action. 1218 // Only call this function yourself not with user input or make sure to check the string yourself. 1219 // Ref: https://github.com/raysan5/raylib/issues/686 1220 void OpenURL(const char *url) 1221 { 1222 // Security check to (partially) avoid malicious code 1223 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); 1224 else SDL_OpenURL(url); 1225 } 1226 1227 //---------------------------------------------------------------------------------- 1228 // Module Functions Definition: Inputs 1229 //---------------------------------------------------------------------------------- 1230 1231 // Set internal gamepad mappings 1232 int SetGamepadMappings(const char *mappings) 1233 { 1234 return SDL_GameControllerAddMapping(mappings); 1235 } 1236 1237 // Set gamepad vibration 1238 void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration) 1239 { 1240 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (duration > 0.0f)) 1241 { 1242 if (leftMotor < 0.0f) leftMotor = 0.0f; 1243 if (leftMotor > 1.0f) leftMotor = 1.0f; 1244 if (rightMotor < 0.0f) rightMotor = 0.0f; 1245 if (rightMotor > 1.0f) rightMotor = 1.0f; 1246 if (duration > MAX_GAMEPAD_VIBRATION_TIME) duration = MAX_GAMEPAD_VIBRATION_TIME; 1247 1248 SDL_GameControllerRumble(platform.gamepad[gamepad], (Uint16)(leftMotor*65535.0f), (Uint16)(rightMotor*65535.0f), (Uint32)(duration*1000.0f)); 1249 } 1250 } 1251 1252 // Set mouse position XY 1253 void SetMousePosition(int x, int y) 1254 { 1255 SDL_WarpMouseInWindow(platform.window, x, y); 1256 1257 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; 1258 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; 1259 } 1260 1261 // Set mouse cursor 1262 void SetMouseCursor(int cursor) 1263 { 1264 platform.cursor = SDL_CreateSystemCursor(CursorsLUT[cursor]); 1265 SDL_SetCursor(platform.cursor); 1266 1267 CORE.Input.Mouse.cursor = cursor; 1268 } 1269 1270 // Get physical key name. 1271 const char *GetKeyName(int key) 1272 { 1273 return SDL_GetKeyName(key); 1274 } 1275 1276 static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event) 1277 { 1278 #ifdef PLATFORM_DESKTOP_SDL3 // SDL3 1279 int count = 0; 1280 SDL_Finger **fingers = SDL_GetTouchFingers(event.touchID, &count); 1281 CORE.Input.Touch.pointCount = count; 1282 1283 for (int i = 0; i < CORE.Input.Touch.pointCount; i++) 1284 { 1285 SDL_Finger *finger = fingers[i]; 1286 CORE.Input.Touch.pointId[i] = finger->id; 1287 CORE.Input.Touch.position[i].x = finger->x*CORE.Window.screen.width; 1288 CORE.Input.Touch.position[i].y = finger->y*CORE.Window.screen.height; 1289 CORE.Input.Touch.currentTouchState[i] = 1; 1290 } 1291 SDL_free(fingers); 1292 #else // SDL2 1293 1294 CORE.Input.Touch.pointCount = SDL_GetNumTouchFingers(event.touchId); 1295 1296 for (int i = 0; i < CORE.Input.Touch.pointCount; i++) 1297 { 1298 SDL_Finger *finger = SDL_GetTouchFinger(event.touchId, i); 1299 CORE.Input.Touch.pointId[i] = finger->id; 1300 CORE.Input.Touch.position[i].x = finger->x*CORE.Window.screen.width; 1301 CORE.Input.Touch.position[i].y = finger->y*CORE.Window.screen.height; 1302 CORE.Input.Touch.currentTouchState[i] = 1; 1303 } 1304 #endif 1305 1306 for (int i = CORE.Input.Touch.pointCount; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.currentTouchState[i] = 0; 1307 } 1308 1309 // Register all input events 1310 void PollInputEvents(void) 1311 { 1312 #if defined(SUPPORT_GESTURES_SYSTEM) 1313 // NOTE: Gestures update must be called every frame to reset gestures correctly 1314 // because ProcessGestureEvent() is just called on an event, not every frame 1315 UpdateGestures(); 1316 #endif 1317 1318 // Reset keys/chars pressed registered 1319 CORE.Input.Keyboard.keyPressedQueueCount = 0; 1320 CORE.Input.Keyboard.charPressedQueueCount = 0; 1321 1322 // Reset mouse wheel 1323 CORE.Input.Mouse.currentWheelMove.x = 0; 1324 CORE.Input.Mouse.currentWheelMove.y = 0; 1325 1326 // Register previous mouse position 1327 if (platform.cursorRelative) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; 1328 else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; 1329 1330 // Reset last gamepad button/axis registered state 1331 for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++) 1332 { 1333 // Check if gamepad is available 1334 if (CORE.Input.Gamepad.ready[i]) 1335 { 1336 // Register previous gamepad button states 1337 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) 1338 { 1339 CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; 1340 } 1341 } 1342 } 1343 1344 // Register previous touch states 1345 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; 1346 1347 // Map touch position to mouse position for convenience 1348 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; 1349 1350 int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE 1351 bool realTouch = false; // Flag to differentiate real touch gestures from mouse ones 1352 1353 // Register previous keys states 1354 // NOTE: Android supports up to 260 keys 1355 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) 1356 { 1357 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; 1358 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; 1359 } 1360 1361 // Register previous mouse states 1362 for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; 1363 1364 // Poll input events for current platform 1365 //----------------------------------------------------------------------------- 1366 /* 1367 // WARNING: Indexes into this array are obtained by using SDL_Scancode values, not SDL_Keycode values 1368 const Uint8 *keys = SDL_GetKeyboardState(NULL); 1369 for (int i = 0; i < 256; ++i) 1370 { 1371 CORE.Input.Keyboard.currentKeyState[i] = keys[i]; 1372 //if (keys[i]) TRACELOG(LOG_WARNING, "Pressed key: %i", i); 1373 } 1374 */ 1375 1376 CORE.Window.resizedLastFrame = false; 1377 1378 SDL_Event event = { 0 }; 1379 while (SDL_PollEvent(&event) != 0) 1380 { 1381 // All input events can be processed after polling 1382 switch (event.type) 1383 { 1384 case SDL_QUIT: CORE.Window.shouldClose = true; break; 1385 1386 case SDL_DROPFILE: // Dropped file 1387 { 1388 if (CORE.Window.dropFileCount == 0) 1389 { 1390 // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files 1391 // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed 1392 // TODO: Pointers should probably be reallocated for any new file added... 1393 CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *)); 1394 1395 CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); 1396 #ifdef PLATFORM_DESKTOP_SDL3 1397 // const char *data; /**< The text for SDL_EVENT_DROP_TEXT and the file name for SDL_EVENT_DROP_FILE, NULL for other events */ 1398 // Event memory is now managed by SDL, so you should not free the data in SDL_EVENT_DROP_FILE, and if you want to hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events, you should make a copy of it. SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed. 1399 strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data); 1400 #else 1401 strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); 1402 SDL_free(event.drop.file); 1403 #endif 1404 1405 CORE.Window.dropFileCount++; 1406 } 1407 else if (CORE.Window.dropFileCount < 1024) 1408 { 1409 CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); 1410 #ifdef PLATFORM_DESKTOP_SDL3 1411 strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data); 1412 #else 1413 strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); 1414 SDL_free(event.drop.file); 1415 #endif 1416 1417 CORE.Window.dropFileCount++; 1418 } 1419 else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!"); 1420 1421 } break; 1422 1423 // Window events are also polled (Minimized, maximized, close...) 1424 1425 #ifndef PLATFORM_DESKTOP_SDL3 1426 // SDL3 states: 1427 // The SDL_WINDOWEVENT_* events have been moved to top level events, 1428 // and SDL_WINDOWEVENT has been removed. 1429 // In general, handling this change just means checking for the individual events instead of first checking for SDL_WINDOWEVENT 1430 // and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event. 1431 case SDL_WINDOWEVENT: 1432 { 1433 switch (event.window.event) 1434 { 1435 #endif 1436 case SDL_WINDOWEVENT_RESIZED: 1437 case SDL_WINDOWEVENT_SIZE_CHANGED: 1438 { 1439 const int width = event.window.data1; 1440 const int height = event.window.data2; 1441 SetupViewport(width, height); 1442 CORE.Window.screen.width = width; 1443 CORE.Window.screen.height = height; 1444 CORE.Window.currentFbo.width = width; 1445 CORE.Window.currentFbo.height = height; 1446 CORE.Window.resizedLastFrame = true; 1447 } break; 1448 case SDL_WINDOWEVENT_ENTER: 1449 { 1450 CORE.Input.Mouse.cursorOnScreen = true; 1451 } break; 1452 case SDL_WINDOWEVENT_LEAVE: 1453 { 1454 CORE.Input.Mouse.cursorOnScreen = false; 1455 } break; 1456 case SDL_WINDOWEVENT_HIDDEN: 1457 case SDL_WINDOWEVENT_MINIMIZED: 1458 case SDL_WINDOWEVENT_FOCUS_LOST: 1459 case SDL_WINDOWEVENT_SHOWN: 1460 case SDL_WINDOWEVENT_FOCUS_GAINED: 1461 case SDL_WINDOWEVENT_MAXIMIZED: 1462 case SDL_WINDOWEVENT_RESTORED: 1463 #ifdef PLATFORM_DESKTOP_SDL3 1464 break; 1465 #else 1466 default: break; 1467 } 1468 } break; 1469 #endif 1470 1471 // Keyboard events 1472 case SDL_KEYDOWN: 1473 { 1474 #ifdef PLATFORM_DESKTOP_SDL3 1475 // SDL3 Migration: The following structures have been removed: * SDL_Keysym 1476 KeyboardKey key = ConvertScancodeToKey(event.key.scancode); 1477 #else 1478 KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); 1479 #endif 1480 1481 if (key != KEY_NULL) 1482 { 1483 // If key was up, add it to the key pressed queue 1484 if ((CORE.Input.Keyboard.currentKeyState[key] == 0) && (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE)) 1485 { 1486 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; 1487 CORE.Input.Keyboard.keyPressedQueueCount++; 1488 } 1489 1490 CORE.Input.Keyboard.currentKeyState[key] = 1; 1491 } 1492 1493 if (event.key.repeat) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; 1494 1495 // TODO: Put exitKey verification outside the switch? 1496 if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) 1497 { 1498 CORE.Window.shouldClose = true; 1499 } 1500 } break; 1501 1502 case SDL_KEYUP: 1503 { 1504 1505 #ifdef PLATFORM_DESKTOP_SDL3 1506 KeyboardKey key = ConvertScancodeToKey(event.key.scancode); 1507 #else 1508 KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); 1509 #endif 1510 if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0; 1511 } break; 1512 1513 case SDL_TEXTINPUT: 1514 { 1515 // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int) 1516 1517 int codepointSize = 0; 1518 1519 // Check if there is space available in the queue 1520 if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) 1521 { 1522 // Add character (codepoint) to the queue 1523 CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = GetCodepointNext(event.text.text, &codepointSize); 1524 CORE.Input.Keyboard.charPressedQueueCount++; 1525 } 1526 } break; 1527 1528 // Check mouse events 1529 case SDL_MOUSEBUTTONDOWN: 1530 { 1531 // NOTE: SDL2 mouse button order is LEFT, MIDDLE, RIGHT, but raylib uses LEFT, RIGHT, MIDDLE like GLFW 1532 // The following conditions align SDL with raylib.h MouseButton enum order 1533 int btn = event.button.button - 1; 1534 if (btn == 2) btn = 1; 1535 else if (btn == 1) btn = 2; 1536 1537 CORE.Input.Mouse.currentButtonState[btn] = 1; 1538 CORE.Input.Touch.currentTouchState[btn] = 1; 1539 1540 touchAction = 1; 1541 } break; 1542 case SDL_MOUSEBUTTONUP: 1543 { 1544 // NOTE: SDL2 mouse button order is LEFT, MIDDLE, RIGHT, but raylib uses LEFT, RIGHT, MIDDLE like GLFW 1545 // The following conditions align SDL with raylib.h MouseButton enum order 1546 int btn = event.button.button - 1; 1547 if (btn == 2) btn = 1; 1548 else if (btn == 1) btn = 2; 1549 1550 CORE.Input.Mouse.currentButtonState[btn] = 0; 1551 CORE.Input.Touch.currentTouchState[btn] = 0; 1552 1553 touchAction = 0; 1554 } break; 1555 case SDL_MOUSEWHEEL: 1556 { 1557 CORE.Input.Mouse.currentWheelMove.x = (float)event.wheel.x; 1558 CORE.Input.Mouse.currentWheelMove.y = (float)event.wheel.y; 1559 } break; 1560 case SDL_MOUSEMOTION: 1561 { 1562 if (platform.cursorRelative) 1563 { 1564 CORE.Input.Mouse.currentPosition.x = (float)event.motion.xrel; 1565 CORE.Input.Mouse.currentPosition.y = (float)event.motion.yrel; 1566 CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; 1567 } 1568 else 1569 { 1570 CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; 1571 CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; 1572 } 1573 1574 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; 1575 touchAction = 2; 1576 } break; 1577 1578 case SDL_FINGERDOWN: 1579 { 1580 UpdateTouchPointsSDL(event.tfinger); 1581 touchAction = 1; 1582 realTouch = true; 1583 } break; 1584 case SDL_FINGERUP: 1585 { 1586 UpdateTouchPointsSDL(event.tfinger); 1587 touchAction = 0; 1588 realTouch = true; 1589 } break; 1590 case SDL_FINGERMOTION: 1591 { 1592 UpdateTouchPointsSDL(event.tfinger); 1593 touchAction = 2; 1594 realTouch = true; 1595 } break; 1596 1597 // Check gamepad events 1598 case SDL_JOYDEVICEADDED: 1599 { 1600 int jid = event.jdevice.which; 1601 1602 if (!CORE.Input.Gamepad.ready[jid] && (jid < MAX_GAMEPADS)) 1603 { 1604 platform.gamepad[jid] = SDL_GameControllerOpen(jid); 1605 1606 if (platform.gamepad[jid]) 1607 { 1608 CORE.Input.Gamepad.ready[jid] = true; 1609 CORE.Input.Gamepad.axisCount[jid] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[jid])); 1610 CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; 1611 CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; 1612 strncpy(CORE.Input.Gamepad.name[jid], SDL_GameControllerNameForIndex(jid), 63); 1613 CORE.Input.Gamepad.name[jid][63] = '\0'; 1614 } 1615 else 1616 { 1617 TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError()); 1618 } 1619 } 1620 } break; 1621 case SDL_JOYDEVICEREMOVED: 1622 { 1623 int jid = event.jdevice.which; 1624 1625 if (jid == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[jid]))) 1626 { 1627 SDL_GameControllerClose(platform.gamepad[jid]); 1628 platform.gamepad[jid] = SDL_GameControllerOpen(0); 1629 CORE.Input.Gamepad.ready[jid] = false; 1630 memset(CORE.Input.Gamepad.name[jid], 0, 64); 1631 } 1632 } break; 1633 case SDL_CONTROLLERBUTTONDOWN: 1634 { 1635 int button = -1; 1636 1637 switch (event.jbutton.button) 1638 { 1639 case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; 1640 case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; 1641 case SDL_CONTROLLER_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; 1642 case SDL_CONTROLLER_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; 1643 1644 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; 1645 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; 1646 1647 case SDL_CONTROLLER_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; 1648 case SDL_CONTROLLER_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break; 1649 case SDL_CONTROLLER_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; 1650 1651 case SDL_CONTROLLER_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; 1652 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; 1653 case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; 1654 case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; 1655 1656 case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = GAMEPAD_BUTTON_LEFT_THUMB; break; 1657 case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; 1658 default: break; 1659 } 1660 1661 if (button >= 0) 1662 { 1663 CORE.Input.Gamepad.currentButtonState[event.jbutton.which][button] = 1; 1664 CORE.Input.Gamepad.lastButtonPressed = button; 1665 } 1666 } break; 1667 case SDL_CONTROLLERBUTTONUP: 1668 { 1669 int button = -1; 1670 1671 switch (event.jbutton.button) 1672 { 1673 case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; 1674 case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; 1675 case SDL_CONTROLLER_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; 1676 case SDL_CONTROLLER_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; 1677 1678 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; 1679 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; 1680 1681 case SDL_CONTROLLER_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; 1682 case SDL_CONTROLLER_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break; 1683 case SDL_CONTROLLER_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; 1684 1685 case SDL_CONTROLLER_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; 1686 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; 1687 case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; 1688 case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; 1689 1690 case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = GAMEPAD_BUTTON_LEFT_THUMB; break; 1691 case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; 1692 default: break; 1693 } 1694 1695 if (button >= 0) 1696 { 1697 CORE.Input.Gamepad.currentButtonState[event.jbutton.which][button] = 0; 1698 if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; 1699 } 1700 } break; 1701 case SDL_CONTROLLERAXISMOTION: 1702 { 1703 int axis = -1; 1704 1705 switch (event.jaxis.axis) 1706 { 1707 case SDL_CONTROLLER_AXIS_LEFTX: axis = GAMEPAD_AXIS_LEFT_X; break; 1708 case SDL_CONTROLLER_AXIS_LEFTY: axis = GAMEPAD_AXIS_LEFT_Y; break; 1709 case SDL_CONTROLLER_AXIS_RIGHTX: axis = GAMEPAD_AXIS_RIGHT_X; break; 1710 case SDL_CONTROLLER_AXIS_RIGHTY: axis = GAMEPAD_AXIS_RIGHT_Y; break; 1711 case SDL_CONTROLLER_AXIS_TRIGGERLEFT: axis = GAMEPAD_AXIS_LEFT_TRIGGER; break; 1712 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: axis = GAMEPAD_AXIS_RIGHT_TRIGGER; break; 1713 default: break; 1714 } 1715 1716 if (axis >= 0) 1717 { 1718 // SDL axis value range is -32768 to 32767, we normalize it to RayLib's -1.0 to 1.0f range 1719 float value = event.jaxis.value/(float)32767; 1720 CORE.Input.Gamepad.axisState[event.jaxis.which][axis] = value; 1721 1722 // Register button state for triggers in addition to their axes 1723 if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER)) 1724 { 1725 int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2; 1726 int pressed = (value > 0.1f); 1727 CORE.Input.Gamepad.currentButtonState[event.jaxis.which][button] = pressed; 1728 if (pressed) CORE.Input.Gamepad.lastButtonPressed = button; 1729 else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0; 1730 } 1731 } 1732 } break; 1733 default: break; 1734 } 1735 1736 #if defined(SUPPORT_GESTURES_SYSTEM) 1737 if (touchAction > -1) 1738 { 1739 // Process mouse events as touches to be able to use mouse-gestures 1740 GestureEvent gestureEvent = { 0 }; 1741 1742 // Register touch actions 1743 gestureEvent.touchAction = touchAction; 1744 1745 // Assign a pointer ID 1746 gestureEvent.pointId[0] = 0; 1747 1748 // Register touch points count 1749 gestureEvent.pointCount = 1; 1750 1751 // Register touch points position, only one point registered 1752 if (touchAction == 2 || realTouch) gestureEvent.position[0] = CORE.Input.Touch.position[0]; 1753 else gestureEvent.position[0] = GetMousePosition(); 1754 1755 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height 1756 gestureEvent.position[0].x /= (float)GetScreenWidth(); 1757 gestureEvent.position[0].y /= (float)GetScreenHeight(); 1758 1759 // Gesture data is sent to gestures-system for processing 1760 ProcessGestureEvent(gestureEvent); 1761 1762 touchAction = -1; 1763 } 1764 #endif 1765 } 1766 //----------------------------------------------------------------------------- 1767 } 1768 1769 //---------------------------------------------------------------------------------- 1770 // Module Internal Functions Definition 1771 //---------------------------------------------------------------------------------- 1772 1773 // Initialize platform: graphics, inputs and more 1774 int InitPlatform(void) 1775 { 1776 // Initialize SDL internal global state, only required systems 1777 // NOTE: Not all systems need to be initialized, SDL_INIT_AUDIO is not required, managed by miniaudio 1778 int result = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER); 1779 if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; } 1780 1781 // Initialize graphic device: display/window and graphic context 1782 //---------------------------------------------------------------------------- 1783 unsigned int flags = 0; 1784 flags |= SDL_WINDOW_SHOWN; 1785 flags |= SDL_WINDOW_OPENGL; 1786 flags |= SDL_WINDOW_INPUT_FOCUS; 1787 flags |= SDL_WINDOW_MOUSE_FOCUS; 1788 flags |= SDL_WINDOW_MOUSE_CAPTURE; // Window has mouse captured 1789 1790 // Check window creation flags 1791 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) 1792 { 1793 CORE.Window.fullscreen = true; 1794 flags |= SDL_WINDOW_FULLSCREEN; 1795 } 1796 1797 //if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) flags |= SDL_WINDOW_HIDDEN; 1798 if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= SDL_WINDOW_BORDERLESS; 1799 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) flags |= SDL_WINDOW_RESIZABLE; 1800 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) flags |= SDL_WINDOW_MINIMIZED; 1801 if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= SDL_WINDOW_MAXIMIZED; 1802 1803 if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) 1804 { 1805 flags &= ~SDL_WINDOW_INPUT_FOCUS; 1806 flags &= ~SDL_WINDOW_MOUSE_FOCUS; 1807 } 1808 1809 if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) flags |= SDL_WINDOW_ALWAYS_ON_TOP; 1810 if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) flags &= ~SDL_WINDOW_MOUSE_CAPTURE; 1811 1812 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) flags |= SDL_WINDOW_ALLOW_HIGHDPI; 1813 1814 //if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= SDL_WINDOW_TRANSPARENT; // Alternative: SDL_GL_ALPHA_SIZE = 8 1815 1816 //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; 1817 1818 // NOTE: Some OpenGL context attributes must be set before window creation 1819 1820 // Check selection OpenGL version 1821 if (rlGetVersion() == RL_OPENGL_21) 1822 { 1823 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); 1824 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); 1825 } 1826 else if (rlGetVersion() == RL_OPENGL_33) 1827 { 1828 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 1829 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 1830 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 1831 } 1832 else if (rlGetVersion() == RL_OPENGL_43) 1833 { 1834 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 1835 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 1836 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 1837 #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) 1838 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); // Enable OpenGL Debug Context 1839 #endif 1840 } 1841 else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context 1842 { 1843 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); 1844 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); 1845 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); 1846 } 1847 else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context 1848 { 1849 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); 1850 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 1851 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); 1852 } 1853 1854 if (CORE.Window.flags & FLAG_MSAA_4X_HINT) 1855 { 1856 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); 1857 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); 1858 } 1859 1860 // Init window 1861 #ifdef PLATFORM_DESKTOP_SDL3 1862 platform.window = SDL_CreateWindow(CORE.Window.title, CORE.Window.screen.width, CORE.Window.screen.height, flags); 1863 #else 1864 platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags); 1865 #endif 1866 1867 // Init OpenGL context 1868 platform.glContext = SDL_GL_CreateContext(platform.window); 1869 1870 // Check window and glContext have been initialized successfully 1871 if ((platform.window != NULL) && (platform.glContext != NULL)) 1872 { 1873 CORE.Window.ready = true; 1874 1875 SDL_DisplayMode displayMode = { 0 }; 1876 SDL_GetCurrentDisplayMode(GetCurrentMonitor(), &displayMode); 1877 1878 CORE.Window.display.width = displayMode.w; 1879 CORE.Window.display.height = displayMode.h; 1880 1881 CORE.Window.render.width = CORE.Window.screen.width; 1882 CORE.Window.render.height = CORE.Window.screen.height; 1883 CORE.Window.currentFbo.width = CORE.Window.render.width; 1884 CORE.Window.currentFbo.height = CORE.Window.render.height; 1885 1886 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); 1887 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); 1888 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); 1889 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); 1890 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); 1891 1892 if (CORE.Window.flags & FLAG_VSYNC_HINT) SDL_GL_SetSwapInterval(1); 1893 else SDL_GL_SetSwapInterval(0); 1894 } 1895 else 1896 { 1897 TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); 1898 return -1; 1899 } 1900 1901 // Load OpenGL extensions 1902 // NOTE: GL procedures address loader is required to load extensions 1903 rlLoadExtensions(SDL_GL_GetProcAddress); 1904 //---------------------------------------------------------------------------- 1905 1906 // Initialize input events system 1907 //---------------------------------------------------------------------------- 1908 // Initialize gamepads 1909 for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++) 1910 { 1911 platform.gamepad[i] = SDL_GameControllerOpen(i); 1912 1913 if (platform.gamepad[i]) 1914 { 1915 CORE.Input.Gamepad.ready[i] = true; 1916 CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i])); 1917 CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f; 1918 CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f; 1919 strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), 63); 1920 CORE.Input.Gamepad.name[i][63] = '\0'; 1921 } 1922 else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError()); 1923 } 1924 1925 // Disable mouse events being interpreted as touch events 1926 // NOTE: This is wanted because there are SDL_FINGER* events available which provide unique data 1927 // Due to the way PollInputEvents() and rgestures.h are currently implemented, setting this won't break SUPPORT_MOUSE_GESTURES 1928 SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); 1929 1930 SDL_EventState(SDL_DROPFILE, SDL_ENABLE); 1931 //---------------------------------------------------------------------------- 1932 1933 // Initialize timing system 1934 //---------------------------------------------------------------------------- 1935 // NOTE: No need to call InitTimer(), let SDL manage it internally 1936 CORE.Time.previous = GetTime(); // Get time as double 1937 1938 #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) 1939 SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod() 1940 #endif 1941 //---------------------------------------------------------------------------- 1942 1943 // Initialize storage system 1944 //---------------------------------------------------------------------------- 1945 // Define base path for storage 1946 CORE.Storage.basePath = SDL_GetBasePath(); // Alternative: GetWorkingDirectory(); 1947 //---------------------------------------------------------------------------- 1948 1949 1950 #ifdef PLATFORM_DESKTOP_SDL3 1951 TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL3): Initialized successfully"); 1952 #else 1953 TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully"); 1954 #endif 1955 1956 return 0; 1957 } 1958 1959 // Close platform 1960 void ClosePlatform(void) 1961 { 1962 SDL_FreeCursor(platform.cursor); // Free cursor 1963 SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context 1964 SDL_DestroyWindow(platform.window); 1965 SDL_Quit(); // Deinitialize SDL internal global state 1966 } 1967 1968 // Scancode to keycode mapping 1969 static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) 1970 { 1971 if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) 1972 { 1973 return mapScancodeToKey[sdlScancode]; 1974 } 1975 1976 return KEY_NULL; // No equivalent key in Raylib 1977 } 1978 // EOF