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