aboutsummaryrefslogtreecommitdiff
path: root/raylib/src/platforms
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-04-21 11:09:56 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2025-04-21 11:09:56 +0200
commit123144c93bfc77883c8fb517828b47bbe13b8671 (patch)
tree762739afedb5f3f168051367515cb9b9a06ec68d /raylib/src/platforms
downloadminesweeper-123144c93bfc77883c8fb517828b47bbe13b8671.tar.gz
minesweeper-123144c93bfc77883c8fb517828b47bbe13b8671.zip
Initial commitHEADmaster
Diffstat (limited to 'raylib/src/platforms')
-rw-r--r--raylib/src/platforms/rcore_android.c1339
-rw-r--r--raylib/src/platforms/rcore_desktop_glfw.c1933
-rw-r--r--raylib/src/platforms/rcore_desktop_rgfw.c1387
-rw-r--r--raylib/src/platforms/rcore_desktop_sdl.c1978
-rw-r--r--raylib/src/platforms/rcore_drm.c1942
-rw-r--r--raylib/src/platforms/rcore_template.c597
-rw-r--r--raylib/src/platforms/rcore_web.c1792
7 files changed, 10968 insertions, 0 deletions
diff --git a/raylib/src/platforms/rcore_android.c b/raylib/src/platforms/rcore_android.c
new file mode 100644
index 0000000..85ce82a
--- /dev/null
+++ b/raylib/src/platforms/rcore_android.c
@@ -0,0 +1,1339 @@
1/**********************************************************************************************
2*
3* rcore_android - Functions to manage window, graphics device and inputs
4*
5* PLATFORM: ANDROID
6* - Android (ARM, ARM64)
7*
8* LIMITATIONS:
9* - Limitation 01
10* - Limitation 02
11*
12* POSSIBLE IMPROVEMENTS:
13* - Improvement 01
14* - Improvement 02
15*
16* ADDITIONAL NOTES:
17* - TRACELOG() function is located in raylib [utils] module
18*
19* CONFIGURATION:
20* #define RCORE_PLATFORM_CUSTOM_FLAG
21* Custom flag for rcore on target platform -not used-
22*
23* DEPENDENCIES:
24* - Android NDK: Provides C API to access Android functionality
25* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
26*
27*
28* LICENSE: zlib/libpng
29*
30* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
31*
32* This software is provided "as-is", without any express or implied warranty. In no event
33* will the authors be held liable for any damages arising from the use of this software.
34*
35* Permission is granted to anyone to use this software for any purpose, including commercial
36* applications, and to alter it and redistribute it freely, subject to the following restrictions:
37*
38* 1. The origin of this software must not be misrepresented; you must not claim that you
39* wrote the original software. If you use this software in a product, an acknowledgment
40* in the product documentation would be appreciated but is not required.
41*
42* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
43* as being the original software.
44*
45* 3. This notice may not be removed or altered from any source distribution.
46*
47**********************************************************************************************/
48
49#include <android_native_app_glue.h> // Required for: android_app struct and activity management
50#include <android/window.h> // Required for: AWINDOW_FLAG_FULLSCREEN definition and others
51//#include <android/sensor.h> // Required for: Android sensors functions (accelerometer, gyroscope, light...)
52#include <jni.h> // Required for: JNIEnv and JavaVM [Used in OpenURL()]
53
54#include <EGL/egl.h> // Native platform windowing system interface
55
56//----------------------------------------------------------------------------------
57// Types and Structures Definition
58//----------------------------------------------------------------------------------
59typedef struct {
60 // Application data
61 struct android_app *app; // Android activity
62 struct android_poll_source *source; // Android events polling source
63 bool appEnabled; // Flag to detect if app is active ** = true
64 bool contextRebindRequired; // Used to know context rebind required
65
66 // Display data
67 EGLDisplay device; // Native display device (physical screen connection)
68 EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
69 EGLContext context; // Graphic context, mode in which drawing can be done
70 EGLConfig config; // Graphic config
71} PlatformData;
72
73//----------------------------------------------------------------------------------
74// Global Variables Definition
75//----------------------------------------------------------------------------------
76extern CoreData CORE; // Global CORE state context
77extern bool isGpuReady; // Flag to note GPU has been initialized successfully
78static PlatformData platform = { 0 }; // Platform specific data
79
80//----------------------------------------------------------------------------------
81// Local Variables Definition
82//----------------------------------------------------------------------------------
83#define KEYCODE_MAP_SIZE 162
84static const KeyboardKey mapKeycode[KEYCODE_MAP_SIZE] = {
85 KEY_NULL, // AKEYCODE_UNKNOWN
86 0, // AKEYCODE_SOFT_LEFT
87 0, // AKEYCODE_SOFT_RIGHT
88 0, // AKEYCODE_HOME
89 KEY_BACK, // AKEYCODE_BACK
90 0, // AKEYCODE_CALL
91 0, // AKEYCODE_ENDCALL
92 KEY_ZERO, // AKEYCODE_0
93 KEY_ONE, // AKEYCODE_1
94 KEY_TWO, // AKEYCODE_2
95 KEY_THREE, // AKEYCODE_3
96 KEY_FOUR, // AKEYCODE_4
97 KEY_FIVE, // AKEYCODE_5
98 KEY_SIX, // AKEYCODE_6
99 KEY_SEVEN, // AKEYCODE_7
100 KEY_EIGHT, // AKEYCODE_8
101 KEY_NINE, // AKEYCODE_9
102 0, // AKEYCODE_STAR
103 0, // AKEYCODE_POUND
104 KEY_UP, // AKEYCODE_DPAD_UP
105 KEY_DOWN, // AKEYCODE_DPAD_DOWN
106 KEY_LEFT, // AKEYCODE_DPAD_LEFT
107 KEY_RIGHT, // AKEYCODE_DPAD_RIGHT
108 0, // AKEYCODE_DPAD_CENTER
109 KEY_VOLUME_UP, // AKEYCODE_VOLUME_UP
110 KEY_VOLUME_DOWN, // AKEYCODE_VOLUME_DOWN
111 0, // AKEYCODE_POWER
112 0, // AKEYCODE_CAMERA
113 0, // AKEYCODE_CLEAR
114 KEY_A, // AKEYCODE_A
115 KEY_B, // AKEYCODE_B
116 KEY_C, // AKEYCODE_C
117 KEY_D, // AKEYCODE_D
118 KEY_E, // AKEYCODE_E
119 KEY_F, // AKEYCODE_F
120 KEY_G, // AKEYCODE_G
121 KEY_H, // AKEYCODE_H
122 KEY_I, // AKEYCODE_I
123 KEY_J, // AKEYCODE_J
124 KEY_K, // AKEYCODE_K
125 KEY_L, // AKEYCODE_L
126 KEY_M, // AKEYCODE_M
127 KEY_N, // AKEYCODE_N
128 KEY_O, // AKEYCODE_O
129 KEY_P, // AKEYCODE_P
130 KEY_Q, // AKEYCODE_Q
131 KEY_R, // AKEYCODE_R
132 KEY_S, // AKEYCODE_S
133 KEY_T, // AKEYCODE_T
134 KEY_U, // AKEYCODE_U
135 KEY_V, // AKEYCODE_V
136 KEY_W, // AKEYCODE_W
137 KEY_X, // AKEYCODE_X
138 KEY_Y, // AKEYCODE_Y
139 KEY_Z, // AKEYCODE_Z
140 KEY_COMMA, // AKEYCODE_COMMA
141 KEY_PERIOD, // AKEYCODE_PERIOD
142 KEY_LEFT_ALT, // AKEYCODE_ALT_LEFT
143 KEY_RIGHT_ALT, // AKEYCODE_ALT_RIGHT
144 KEY_LEFT_SHIFT, // AKEYCODE_SHIFT_LEFT
145 KEY_RIGHT_SHIFT, // AKEYCODE_SHIFT_RIGHT
146 KEY_TAB, // AKEYCODE_TAB
147 KEY_SPACE, // AKEYCODE_SPACE
148 0, // AKEYCODE_SYM
149 0, // AKEYCODE_EXPLORER
150 0, // AKEYCODE_ENVELOPE
151 KEY_ENTER, // AKEYCODE_ENTER
152 KEY_BACKSPACE, // AKEYCODE_DEL
153 KEY_GRAVE, // AKEYCODE_GRAVE
154 KEY_MINUS, // AKEYCODE_MINUS
155 KEY_EQUAL, // AKEYCODE_EQUALS
156 KEY_LEFT_BRACKET, // AKEYCODE_LEFT_BRACKET
157 KEY_RIGHT_BRACKET, // AKEYCODE_RIGHT_BRACKET
158 KEY_BACKSLASH, // AKEYCODE_BACKSLASH
159 KEY_SEMICOLON, // AKEYCODE_SEMICOLON
160 KEY_APOSTROPHE, // AKEYCODE_APOSTROPHE
161 KEY_SLASH, // AKEYCODE_SLASH
162 0, // AKEYCODE_AT
163 0, // AKEYCODE_NUM
164 0, // AKEYCODE_HEADSETHOOK
165 0, // AKEYCODE_FOCUS
166 0, // AKEYCODE_PLUS
167 KEY_MENU, // AKEYCODE_MENU
168 0, // AKEYCODE_NOTIFICATION
169 0, // AKEYCODE_SEARCH
170 0, // AKEYCODE_MEDIA_PLAY_PAUSE
171 0, // AKEYCODE_MEDIA_STOP
172 0, // AKEYCODE_MEDIA_NEXT
173 0, // AKEYCODE_MEDIA_PREVIOUS
174 0, // AKEYCODE_MEDIA_REWIND
175 0, // AKEYCODE_MEDIA_FAST_FORWARD
176 0, // AKEYCODE_MUTE
177 KEY_PAGE_UP, // AKEYCODE_PAGE_UP
178 KEY_PAGE_DOWN, // AKEYCODE_PAGE_DOWN
179 0, // AKEYCODE_PICTSYMBOLS
180 0, // AKEYCODE_SWITCH_CHARSET
181 0, // AKEYCODE_BUTTON_A
182 0, // AKEYCODE_BUTTON_B
183 0, // AKEYCODE_BUTTON_C
184 0, // AKEYCODE_BUTTON_X
185 0, // AKEYCODE_BUTTON_Y
186 0, // AKEYCODE_BUTTON_Z
187 0, // AKEYCODE_BUTTON_L1
188 0, // AKEYCODE_BUTTON_R1
189 0, // AKEYCODE_BUTTON_L2
190 0, // AKEYCODE_BUTTON_R2
191 0, // AKEYCODE_BUTTON_THUMBL
192 0, // AKEYCODE_BUTTON_THUMBR
193 0, // AKEYCODE_BUTTON_START
194 0, // AKEYCODE_BUTTON_SELECT
195 0, // AKEYCODE_BUTTON_MODE
196 KEY_ESCAPE, // AKEYCODE_ESCAPE
197 KEY_DELETE, // AKEYCODE_FORWARD_DELL
198 KEY_LEFT_CONTROL, // AKEYCODE_CTRL_LEFT
199 KEY_RIGHT_CONTROL, // AKEYCODE_CTRL_RIGHT
200 KEY_CAPS_LOCK, // AKEYCODE_CAPS_LOCK
201 KEY_SCROLL_LOCK, // AKEYCODE_SCROLL_LOCK
202 KEY_LEFT_SUPER, // AKEYCODE_META_LEFT
203 KEY_RIGHT_SUPER, // AKEYCODE_META_RIGHT
204 0, // AKEYCODE_FUNCTION
205 KEY_PRINT_SCREEN, // AKEYCODE_SYSRQ
206 KEY_PAUSE, // AKEYCODE_BREAK
207 KEY_HOME, // AKEYCODE_MOVE_HOME
208 KEY_END, // AKEYCODE_MOVE_END
209 KEY_INSERT, // AKEYCODE_INSERT
210 0, // AKEYCODE_FORWARD
211 0, // AKEYCODE_MEDIA_PLAY
212 0, // AKEYCODE_MEDIA_PAUSE
213 0, // AKEYCODE_MEDIA_CLOSE
214 0, // AKEYCODE_MEDIA_EJECT
215 0, // AKEYCODE_MEDIA_RECORD
216 KEY_F1, // AKEYCODE_F1
217 KEY_F2, // AKEYCODE_F2
218 KEY_F3, // AKEYCODE_F3
219 KEY_F4, // AKEYCODE_F4
220 KEY_F5, // AKEYCODE_F5
221 KEY_F6, // AKEYCODE_F6
222 KEY_F7, // AKEYCODE_F7
223 KEY_F8, // AKEYCODE_F8
224 KEY_F9, // AKEYCODE_F9
225 KEY_F10, // AKEYCODE_F10
226 KEY_F11, // AKEYCODE_F11
227 KEY_F12, // AKEYCODE_F12
228 KEY_NUM_LOCK, // AKEYCODE_NUM_LOCK
229 KEY_KP_0, // AKEYCODE_NUMPAD_0
230 KEY_KP_1, // AKEYCODE_NUMPAD_1
231 KEY_KP_2, // AKEYCODE_NUMPAD_2
232 KEY_KP_3, // AKEYCODE_NUMPAD_3
233 KEY_KP_4, // AKEYCODE_NUMPAD_4
234 KEY_KP_5, // AKEYCODE_NUMPAD_5
235 KEY_KP_6, // AKEYCODE_NUMPAD_6
236 KEY_KP_7, // AKEYCODE_NUMPAD_7
237 KEY_KP_8, // AKEYCODE_NUMPAD_8
238 KEY_KP_9, // AKEYCODE_NUMPAD_9
239 KEY_KP_DIVIDE, // AKEYCODE_NUMPAD_DIVIDE
240 KEY_KP_MULTIPLY, // AKEYCODE_NUMPAD_MULTIPLY
241 KEY_KP_SUBTRACT, // AKEYCODE_NUMPAD_SUBTRACT
242 KEY_KP_ADD, // AKEYCODE_NUMPAD_ADD
243 KEY_KP_DECIMAL, // AKEYCODE_NUMPAD_DOT
244 0, // AKEYCODE_NUMPAD_COMMA
245 KEY_KP_ENTER, // AKEYCODE_NUMPAD_ENTER
246 KEY_KP_EQUAL // AKEYCODE_NUMPAD_EQUALS
247};
248
249//----------------------------------------------------------------------------------
250// Module Internal Functions Declaration
251//----------------------------------------------------------------------------------
252int InitPlatform(void); // Initialize platform (graphics, inputs and more)
253void ClosePlatform(void); // Close platform
254
255static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands
256static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs
257static GamepadButton AndroidTranslateGamepadButton(int button); // Map Android gamepad button to raylib gamepad button
258
259//----------------------------------------------------------------------------------
260// Module Functions Declaration
261//----------------------------------------------------------------------------------
262// NOTE: Functions declaration is provided by raylib.h
263
264//----------------------------------------------------------------------------------
265// Module Functions Definition: Application
266//----------------------------------------------------------------------------------
267
268// To allow easier porting to android, we allow the user to define a
269// main function which we call from android_main, defined by ourselves
270extern int main(int argc, char *argv[]);
271
272// Android main function
273void android_main(struct android_app *app)
274{
275 char arg0[] = "raylib"; // NOTE: argv[] are mutable
276 platform.app = app;
277
278 // NOTE: Return from main is ignored
279 (void)main(1, (char *[]) { arg0, NULL });
280
281 // Request to end the native activity
282 ANativeActivity_finish(app->activity);
283
284 // Android ALooper_pollOnce() variables
285 int pollResult = 0;
286 int pollEvents = 0;
287
288 // Waiting for application events before complete finishing
289 while (!app->destroyRequested)
290 {
291 // Poll all events until we reach return value TIMEOUT, meaning no events left to process
292 while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void **)&platform.source)) > ALOOPER_POLL_TIMEOUT)
293 {
294 if (platform.source != NULL) platform.source->process(app, platform.source);
295 }
296 }
297}
298
299// NOTE: Add this to header (if apps really need it)
300struct android_app *GetAndroidApp(void)
301{
302 return platform.app;
303}
304
305//----------------------------------------------------------------------------------
306// Module Functions Definition: Window and Graphics Device
307//----------------------------------------------------------------------------------
308
309// Check if application should close
310bool WindowShouldClose(void)
311{
312 if (CORE.Window.ready) return CORE.Window.shouldClose;
313 else return true;
314}
315
316// Toggle fullscreen mode
317void ToggleFullscreen(void)
318{
319 TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
320}
321
322// Toggle borderless windowed mode
323void ToggleBorderlessWindowed(void)
324{
325 TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
326}
327
328// Set window state: maximized, if resizable
329void MaximizeWindow(void)
330{
331 TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
332}
333
334// Set window state: minimized
335void MinimizeWindow(void)
336{
337 TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
338}
339
340// Set window state: not minimized/maximized
341void RestoreWindow(void)
342{
343 TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
344}
345
346// Set window configuration state using flags
347void SetWindowState(unsigned int flags)
348{
349 TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
350}
351
352// Clear window configuration state flags
353void ClearWindowState(unsigned int flags)
354{
355 TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
356}
357
358// Set icon for window
359void SetWindowIcon(Image image)
360{
361 TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
362}
363
364// Set icon for window
365void SetWindowIcons(Image *images, int count)
366{
367 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
368}
369
370// Set title for window
371void SetWindowTitle(const char *title)
372{
373 CORE.Window.title = title;
374}
375
376// Set window position on screen (windowed mode)
377void SetWindowPosition(int x, int y)
378{
379 TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
380}
381
382// Set monitor for the current window
383void SetWindowMonitor(int monitor)
384{
385 TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
386}
387
388// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
389void SetWindowMinSize(int width, int height)
390{
391 CORE.Window.screenMin.width = width;
392 CORE.Window.screenMin.height = height;
393}
394
395// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
396void SetWindowMaxSize(int width, int height)
397{
398 CORE.Window.screenMax.width = width;
399 CORE.Window.screenMax.height = height;
400}
401
402// Set window dimensions
403void SetWindowSize(int width, int height)
404{
405 TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
406}
407
408// Set window opacity, value opacity is between 0.0 and 1.0
409void SetWindowOpacity(float opacity)
410{
411 TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
412}
413
414// Set window focused
415void SetWindowFocused(void)
416{
417 TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
418}
419
420// Get native window handle
421void *GetWindowHandle(void)
422{
423 TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
424 return NULL;
425}
426
427// Get number of monitors
428int GetMonitorCount(void)
429{
430 TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
431 return 1;
432}
433
434// Get number of monitors
435int GetCurrentMonitor(void)
436{
437 TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
438 return 0;
439}
440
441// Get selected monitor position
442Vector2 GetMonitorPosition(int monitor)
443{
444 TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
445 return (Vector2){ 0, 0 };
446}
447
448// Get selected monitor width (currently used by monitor)
449int GetMonitorWidth(int monitor)
450{
451 TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform");
452 return 0;
453}
454
455// Get selected monitor height (currently used by monitor)
456int GetMonitorHeight(int monitor)
457{
458 TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform");
459 return 0;
460}
461
462// Get selected monitor physical width in millimetres
463int GetMonitorPhysicalWidth(int monitor)
464{
465 TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
466 return 0;
467}
468
469// Get selected monitor physical height in millimetres
470int GetMonitorPhysicalHeight(int monitor)
471{
472 TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
473 return 0;
474}
475
476// Get selected monitor refresh rate
477int GetMonitorRefreshRate(int monitor)
478{
479 TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
480 return 0;
481}
482
483// Get the human-readable, UTF-8 encoded name of the selected monitor
484const char *GetMonitorName(int monitor)
485{
486 TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform");
487 return "";
488}
489
490// Get window position XY on monitor
491Vector2 GetWindowPosition(void)
492{
493 TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform");
494 return (Vector2){ 0, 0 };
495}
496
497// Get window scale DPI factor for current monitor
498Vector2 GetWindowScaleDPI(void)
499{
500 TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
501 return (Vector2){ 1.0f, 1.0f };
502}
503
504// Set clipboard text content
505void SetClipboardText(const char *text)
506{
507 TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
508}
509
510// Get clipboard text content
511// NOTE: returned string is allocated and freed by GLFW
512const char *GetClipboardText(void)
513{
514 TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
515 return NULL;
516}
517
518// Show mouse cursor
519void ShowCursor(void)
520{
521 CORE.Input.Mouse.cursorHidden = false;
522}
523
524// Hides mouse cursor
525void HideCursor(void)
526{
527 CORE.Input.Mouse.cursorHidden = true;
528}
529
530// Enables cursor (unlock cursor)
531void EnableCursor(void)
532{
533 // Set cursor position in the middle
534 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
535
536 CORE.Input.Mouse.cursorHidden = false;
537}
538
539// Disables cursor (lock cursor)
540void DisableCursor(void)
541{
542 // Set cursor position in the middle
543 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
544
545 CORE.Input.Mouse.cursorHidden = true;
546}
547
548// Swap back buffer with front buffer (screen drawing)
549void SwapScreenBuffer(void)
550{
551 eglSwapBuffers(platform.device, platform.surface);
552}
553
554//----------------------------------------------------------------------------------
555// Module Functions Definition: Misc
556//----------------------------------------------------------------------------------
557
558// Get elapsed time measure in seconds since InitTimer()
559double GetTime(void)
560{
561 double time = 0.0;
562 struct timespec ts = { 0 };
563 clock_gettime(CLOCK_MONOTONIC, &ts);
564 unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
565
566 time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
567
568 return time;
569}
570
571// Open URL with default system browser (if available)
572// NOTE: This function is only safe to use if you control the URL given.
573// A user could craft a malicious string performing another action.
574// Only call this function yourself not with user input or make sure to check the string yourself.
575// Ref: https://github.com/raysan5/raylib/issues/686
576void OpenURL(const char *url)
577{
578 // Security check to (partially) avoid malicious code
579 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
580 else
581 {
582 JNIEnv *env = NULL;
583 JavaVM *vm = platform.app->activity->vm;
584 (*vm)->AttachCurrentThread(vm, &env, NULL);
585
586 jstring urlString = (*env)->NewStringUTF(env, url);
587 jclass uriClass = (*env)->FindClass(env, "android/net/Uri");
588 jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
589 jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString);
590
591 jclass intentClass = (*env)->FindClass(env, "android/content/Intent");
592 jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;");
593 jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId);
594 jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "<init>", "(Ljava/lang/String;Landroid/net/Uri;)V");
595 jobject intent = (*env)->AllocObject(env, intentClass);
596
597 (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri);
598 jclass activityClass = (*env)->FindClass(env, "android/app/Activity");
599 jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V");
600 (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent);
601
602 (*vm)->DetachCurrentThread(vm);
603 }
604}
605
606//----------------------------------------------------------------------------------
607// Module Functions Definition: Inputs
608//----------------------------------------------------------------------------------
609
610// Set internal gamepad mappings
611int SetGamepadMappings(const char *mappings)
612{
613 TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
614 return 0;
615}
616
617// Set gamepad vibration
618void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
619{
620 TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform");
621}
622
623// Set mouse position XY
624void SetMousePosition(int x, int y)
625{
626 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
627 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
628}
629
630// Set mouse cursor
631void SetMouseCursor(int cursor)
632{
633 TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
634}
635
636// Get physical key name.
637const char *GetKeyName(int key)
638{
639 TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
640 return "";
641}
642
643// Register all input events
644void PollInputEvents(void)
645{
646#if defined(SUPPORT_GESTURES_SYSTEM)
647 // NOTE: Gestures update must be called every frame to reset gestures correctly
648 // because ProcessGestureEvent() is just called on an event, not every frame
649 UpdateGestures();
650#endif
651
652 // Reset keys/chars pressed registered
653 CORE.Input.Keyboard.keyPressedQueueCount = 0;
654 CORE.Input.Keyboard.charPressedQueueCount = 0;
655 // Reset key repeats
656 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
657
658 // Reset last gamepad button/axis registered state
659 CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
660 //CORE.Input.Gamepad.axisCount = 0;
661
662 for (int i = 0; i < MAX_GAMEPADS; i++)
663 {
664 if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available
665 {
666 // Register previous gamepad states
667 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
668 CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
669 }
670 }
671
672 // Register previous touch states
673 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
674
675 // Reset touch positions
676 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
677
678 // Register previous keys states
679 // NOTE: Android supports up to 260 keys
680 for (int i = 0; i < 260; i++)
681 {
682 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
683 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
684 }
685
686 // Android ALooper_pollOnce() variables
687 int pollResult = 0;
688 int pollEvents = 0;
689
690 // Poll Events (registered events) until we reach TIMEOUT which indicates there are no events left to poll
691 // NOTE: Activity is paused if not enabled (platform.appEnabled)
692 while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
693 {
694 // Process this event
695 if (platform.source != NULL) platform.source->process(platform.app, platform.source);
696
697 // NOTE: Allow closing the window in case a configuration change happened.
698 // The android_main function should be allowed to return to its caller in order for the
699 // Android OS to relaunch the activity.
700 if (platform.app->destroyRequested != 0)
701 {
702 CORE.Window.shouldClose = true;
703 }
704 }
705}
706
707//----------------------------------------------------------------------------------
708// Module Internal Functions Definition
709//----------------------------------------------------------------------------------
710
711// Initialize platform: graphics, inputs and more
712int InitPlatform(void)
713{
714 // Initialize display basic configuration
715 //----------------------------------------------------------------------------
716 CORE.Window.currentFbo.width = CORE.Window.screen.width;
717 CORE.Window.currentFbo.height = CORE.Window.screen.height;
718
719 // Set desired windows flags before initializing anything
720 ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
721
722 int orientation = AConfiguration_getOrientation(platform.app->config);
723
724 if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait");
725 else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape");
726
727 // TODO: Automatic orientation doesn't seem to work
728 if (CORE.Window.screen.width <= CORE.Window.screen.height)
729 {
730 AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT);
731 TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait");
732 }
733 else
734 {
735 AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND);
736 TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape");
737 }
738
739 //AConfiguration_getDensity(platform.app->config);
740 //AConfiguration_getKeyboard(platform.app->config);
741 //AConfiguration_getScreenSize(platform.app->config);
742 //AConfiguration_getScreenLong(platform.app->config);
743
744 // Set some default window flags
745 CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false
746 CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false
747 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true
748 CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false
749 //----------------------------------------------------------------------------
750
751 // Initialize App command system
752 // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()...
753 //----------------------------------------------------------------------------
754 platform.app->onAppCmd = AndroidCommandCallback;
755 //----------------------------------------------------------------------------
756
757 // Initialize input events system
758 //----------------------------------------------------------------------------
759 platform.app->onInputEvent = AndroidInputCallback;
760 //----------------------------------------------------------------------------
761
762 // Initialize storage system
763 //----------------------------------------------------------------------------
764 InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize assets manager
765
766 CORE.Storage.basePath = platform.app->activity->internalDataPath; // Define base path for storage
767 //----------------------------------------------------------------------------
768
769 TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Initialized successfully");
770
771 // Android ALooper_pollOnce() variables
772 int pollResult = 0;
773 int pollEvents = 0;
774
775 // Wait for window to be initialized (display and context)
776 while (!CORE.Window.ready)
777 {
778 // Process events until we reach TIMEOUT, which indicates no more events queued.
779 while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
780 {
781 // Process this event
782 if (platform.source != NULL) platform.source->process(platform.app, platform.source);
783
784 // NOTE: It's highly likely destroyRequested will never be non-zero at the start of the activity lifecycle.
785 //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true;
786 }
787 }
788
789 return 0;
790}
791
792// Close platform
793void ClosePlatform(void)
794{
795 // Close surface, context and display
796 if (platform.device != EGL_NO_DISPLAY)
797 {
798 eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
799
800 if (platform.surface != EGL_NO_SURFACE)
801 {
802 eglDestroySurface(platform.device, platform.surface);
803 platform.surface = EGL_NO_SURFACE;
804 }
805
806 if (platform.context != EGL_NO_CONTEXT)
807 {
808 eglDestroyContext(platform.device, platform.context);
809 platform.context = EGL_NO_CONTEXT;
810 }
811
812 eglTerminate(platform.device);
813 platform.device = EGL_NO_DISPLAY;
814 }
815
816 // NOTE: Reset global state in case the activity is being relaunched.
817 if (platform.app->destroyRequested != 0) {
818 CORE = (CoreData){0};
819 platform = (PlatformData){0};
820 }
821}
822
823// Initialize display device and framebuffer
824// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
825// If width or height are 0, default display size will be used for framebuffer size
826// NOTE: returns false in case graphic device could not be created
827static int InitGraphicsDevice(void)
828{
829 CORE.Window.fullscreen = true;
830 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
831
832 EGLint samples = 0;
833 EGLint sampleBuffer = 0;
834 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
835 {
836 samples = 4;
837 sampleBuffer = 1;
838 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
839 }
840
841 const EGLint framebufferAttribs[] =
842 {
843 EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support
844 EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5)
845 EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6)
846 EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5)
847 //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
848 EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
849 //EGL_STENCIL_SIZE, 8, // Stencil buffer size
850 EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
851 EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
852 EGL_NONE
853 };
854
855 const EGLint contextAttribs[] =
856 {
857 EGL_CONTEXT_CLIENT_VERSION, 2,
858 EGL_NONE
859 };
860
861 EGLint numConfigs = 0;
862
863 // Get an EGL device connection
864 platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
865 if (platform.device == EGL_NO_DISPLAY)
866 {
867 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
868 return -1;
869 }
870
871 // Initialize the EGL device connection
872 if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
873 {
874 // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
875 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
876 return -1;
877 }
878
879 // Get an appropriate EGL framebuffer configuration
880 eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs);
881
882 // Set rendering API
883 eglBindAPI(EGL_OPENGL_ES_API);
884
885 // Create an EGL rendering context
886 platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
887 if (platform.context == EGL_NO_CONTEXT)
888 {
889 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
890 return -1;
891 }
892
893 // Create an EGL window surface
894 //---------------------------------------------------------------------------------
895 EGLint displayFormat = 0;
896
897 // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
898 // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
899 eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
900
901 // At this point we need to manage render size vs screen size
902 // NOTE: This function use and modify global module variables:
903 // -> CORE.Window.screen.width/CORE.Window.screen.height
904 // -> CORE.Window.render.width/CORE.Window.render.height
905 // -> CORE.Window.screenScale
906 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
907
908 ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
909 //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size
910
911 platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL);
912
913 // There must be at least one frame displayed before the buffers are swapped
914 //eglSwapInterval(platform.device, 1);
915
916 if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE)
917 {
918 TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface");
919 return -1;
920 }
921 else
922 {
923 CORE.Window.render.width = CORE.Window.screen.width;
924 CORE.Window.render.height = CORE.Window.screen.height;
925 CORE.Window.currentFbo.width = CORE.Window.render.width;
926 CORE.Window.currentFbo.height = CORE.Window.render.height;
927
928 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
929 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
930 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
931 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
932 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
933 }
934
935 // Load OpenGL extensions
936 // NOTE: GL procedures address loader is required to load extensions
937 rlLoadExtensions(eglGetProcAddress);
938
939 CORE.Window.ready = true;
940
941 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
942
943 return 0;
944}
945
946// ANDROID: Process activity lifecycle commands
947static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
948{
949 switch (cmd)
950 {
951 case APP_CMD_START:
952 {
953 //rendering = true;
954 } break;
955 case APP_CMD_RESUME: break;
956 case APP_CMD_INIT_WINDOW:
957 {
958 if (app->window != NULL)
959 {
960 if (platform.contextRebindRequired)
961 {
962 // Reset screen scaling to full display size
963 EGLint displayFormat = 0;
964 eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
965
966 // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the
967 // context rebinding if the screen is scaled unless offsets are added. There's probably a more
968 // appropriate way to fix this
969 ANativeWindow_setBuffersGeometry(app->window,
970 CORE.Window.render.width + CORE.Window.renderOffset.x,
971 CORE.Window.render.height + CORE.Window.renderOffset.y,
972 displayFormat);
973
974 // Recreate display surface and re-attach OpenGL context
975 platform.surface = eglCreateWindowSurface(platform.device, platform.config, app->window, NULL);
976 eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
977
978 platform.contextRebindRequired = false;
979 }
980 else
981 {
982 CORE.Window.display.width = ANativeWindow_getWidth(platform.app->window);
983 CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window);
984
985 // Initialize graphics device (display device and OpenGL context)
986 InitGraphicsDevice();
987
988 // Initialize OpenGL context (states and resources)
989 // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
990 rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
991 isGpuReady = true;
992
993 // Setup default viewport
994 // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
995 SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
996
997 // Initialize hi-res timer
998 InitTimer();
999
1000 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
1001 // Load default font
1002 // WARNING: External function: Module required: rtext
1003 LoadFontDefault();
1004 #if defined(SUPPORT_MODULE_RSHAPES)
1005 // Set font white rectangle for shapes drawing, so shapes and text can be batched together
1006 // WARNING: rshapes module is required, if not available, default internal white rectangle is used
1007 Rectangle rec = GetFontDefault().recs[95];
1008 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
1009 {
1010 // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
1011 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
1012 }
1013 else
1014 {
1015 // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding
1016 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
1017 }
1018 #endif
1019 #else
1020 #if defined(SUPPORT_MODULE_RSHAPES)
1021 // Set default texture and rectangle to be used for shapes drawing
1022 // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
1023 Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
1024 SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes
1025 #endif
1026 #endif
1027
1028 // Initialize random seed
1029 SetRandomSeed((unsigned int)time(NULL));
1030
1031 // TODO: GPU assets reload in case of lost focus (lost context)
1032 // NOTE: This problem has been solved just unbinding and rebinding context from display
1033 /*
1034 if (assetsReloadRequired)
1035 {
1036 for (int i = 0; i < assetCount; i++)
1037 {
1038 // TODO: Unload old asset if required
1039
1040 // Load texture again to pointed texture
1041 (*textureAsset + i) = LoadTexture(assetPath[i]);
1042 }
1043 }
1044 */
1045 }
1046 }
1047 } break;
1048 case APP_CMD_GAINED_FOCUS:
1049 {
1050 platform.appEnabled = true;
1051 CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
1052 //ResumeMusicStream();
1053 } break;
1054 case APP_CMD_PAUSE: break;
1055 case APP_CMD_LOST_FOCUS:
1056 {
1057 platform.appEnabled = false;
1058 CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
1059 //PauseMusicStream();
1060 } break;
1061 case APP_CMD_TERM_WINDOW:
1062 {
1063 // Detach OpenGL context and destroy display surface
1064 // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming.
1065 // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
1066 // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :(
1067 if (platform.device != EGL_NO_DISPLAY)
1068 {
1069 eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1070
1071 if (platform.surface != EGL_NO_SURFACE)
1072 {
1073 eglDestroySurface(platform.device, platform.surface);
1074 platform.surface = EGL_NO_SURFACE;
1075 }
1076
1077 platform.contextRebindRequired = true;
1078 }
1079 // If 'platform.device' is already set to 'EGL_NO_DISPLAY'
1080 // this means that the user has already called 'CloseWindow()'
1081
1082 } break;
1083 case APP_CMD_SAVE_STATE: break;
1084 case APP_CMD_STOP: break;
1085 case APP_CMD_DESTROY: break;
1086 case APP_CMD_CONFIG_CHANGED:
1087 {
1088 //AConfiguration_fromAssetManager(platform.app->config, platform.app->activity->assetManager);
1089 //print_cur_config(platform.app);
1090
1091 // Check screen orientation here!
1092 } break;
1093 default: break;
1094 }
1095}
1096
1097// ANDROID: Map Android gamepad button to raylib gamepad button
1098static GamepadButton AndroidTranslateGamepadButton(int button)
1099{
1100 switch (button)
1101 {
1102 case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN;
1103 case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT;
1104 case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT;
1105 case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP;
1106 case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1;
1107 case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1;
1108 case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2;
1109 case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
1110 case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB;
1111 case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB;
1112 case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT;
1113 case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT;
1114 case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE;
1115 // On some (most?) gamepads dpad events are reported as axis motion instead
1116 case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN;
1117 case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT;
1118 case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT;
1119 case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP;
1120 default: return GAMEPAD_BUTTON_UNKNOWN;
1121 }
1122}
1123
1124// ANDROID: Get input events
1125static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
1126{
1127 // If additional inputs are required check:
1128 // https://developer.android.com/ndk/reference/group/input
1129 // https://developer.android.com/training/game-controllers/controller-input
1130
1131 int type = AInputEvent_getType(event);
1132 int source = AInputEvent_getSource(event);
1133
1134 if (type == AINPUT_EVENT_TYPE_MOTION)
1135 {
1136 if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
1137 ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
1138 {
1139 // For now we'll assume a single gamepad which we "detect" on its input event
1140 CORE.Input.Gamepad.ready[0] = true;
1141
1142 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue(
1143 event, AMOTION_EVENT_AXIS_X, 0);
1144 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue(
1145 event, AMOTION_EVENT_AXIS_Y, 0);
1146 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue(
1147 event, AMOTION_EVENT_AXIS_Z, 0);
1148 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue(
1149 event, AMOTION_EVENT_AXIS_RZ, 0);
1150 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue(
1151 event, AMOTION_EVENT_AXIS_BRAKE, 0)*2.0f - 1.0f;
1152 CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue(
1153 event, AMOTION_EVENT_AXIS_GAS, 0)*2.0f - 1.0f;
1154
1155 // dpad is reported as an axis on android
1156 float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0);
1157 float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0);
1158
1159 if (dpadX == 1.0f)
1160 {
1161 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1;
1162 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0;
1163 }
1164 else if (dpadX == -1.0f)
1165 {
1166 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0;
1167 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1;
1168 }
1169 else
1170 {
1171 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0;
1172 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0;
1173 }
1174
1175 if (dpadY == 1.0f)
1176 {
1177 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1;
1178 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0;
1179 }
1180 else if (dpadY == -1.0f)
1181 {
1182 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0;
1183 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1;
1184 }
1185 else
1186 {
1187 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0;
1188 CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0;
1189 }
1190
1191 return 1; // Handled gamepad axis motion
1192 }
1193 }
1194 else if (type == AINPUT_EVENT_TYPE_KEY)
1195 {
1196 int32_t keycode = AKeyEvent_getKeyCode(event);
1197 //int32_t AKeyEvent_getMetaState(event);
1198
1199 // Handle gamepad button presses and releases
1200 if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
1201 ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
1202 {
1203 // For now we'll assume a single gamepad which we "detect" on its input event
1204 CORE.Input.Gamepad.ready[0] = true;
1205
1206 GamepadButton button = AndroidTranslateGamepadButton(keycode);
1207
1208 if (button == GAMEPAD_BUTTON_UNKNOWN) return 1;
1209
1210 if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
1211 {
1212 CORE.Input.Gamepad.currentButtonState[0][button] = 1;
1213 }
1214 else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up
1215
1216 return 1; // Handled gamepad button
1217 }
1218
1219 KeyboardKey key = (keycode > 0 && keycode < KEYCODE_MAP_SIZE)? mapKeycode[keycode] : KEY_NULL;
1220 if (key != KEY_NULL)
1221 {
1222 // Save current key and its state
1223 // NOTE: Android key action is 0 for down and 1 for up
1224 if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
1225 {
1226 CORE.Input.Keyboard.currentKeyState[key] = 1; // Key down
1227
1228 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
1229 CORE.Input.Keyboard.keyPressedQueueCount++;
1230 }
1231 else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
1232 else CORE.Input.Keyboard.currentKeyState[key] = 0; // Key up
1233 }
1234
1235 if (keycode == AKEYCODE_POWER)
1236 {
1237 // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS
1238 // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS
1239 // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected.
1240 // NOTE: AndroidManifest.xml must have <activity android:configChanges="orientation|keyboardHidden|screenSize" >
1241 // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
1242 return 0;
1243 }
1244 else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU))
1245 {
1246 // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS!
1247 return 1;
1248 }
1249 else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN))
1250 {
1251 // Set default OS behaviour
1252 return 0;
1253 }
1254
1255 return 0;
1256 }
1257
1258 // Register touch points count
1259 CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event);
1260
1261 for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
1262 {
1263 // Register touch points id
1264 CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i);
1265
1266 // Register touch points position
1267 CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
1268
1269 // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height
1270 float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x)/(float)CORE.Window.display.width;
1271 float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y)/(float)CORE.Window.display.height;
1272 CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2;
1273 CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2;
1274 }
1275
1276 int32_t action = AMotionEvent_getAction(event);
1277 unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
1278
1279#if defined(SUPPORT_GESTURES_SYSTEM)
1280 GestureEvent gestureEvent = { 0 };
1281
1282 gestureEvent.pointCount = CORE.Input.Touch.pointCount;
1283
1284 // Register touch actions
1285 if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
1286 else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP;
1287 else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
1288 else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
1289
1290 for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
1291 {
1292 gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
1293 gestureEvent.position[i] = CORE.Input.Touch.position[i];
1294 gestureEvent.position[i].x /= (float)GetScreenWidth();
1295 gestureEvent.position[i].y /= (float)GetScreenHeight();
1296 }
1297
1298 // Gesture data is sent to gestures system for processing
1299 ProcessGestureEvent(gestureEvent);
1300#endif
1301
1302 int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
1303
1304 if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP)
1305 {
1306 // One of the touchpoints is released, remove it from touch point arrays
1307 for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++)
1308 {
1309 CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1];
1310 CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1];
1311 }
1312
1313 CORE.Input.Touch.pointCount--;
1314 }
1315
1316 // When all touchpoints are tapped and released really quickly, this event is generated
1317 if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0;
1318
1319 if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
1320 else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
1321
1322 // Stores the previous position of touch[0] only while it's active to calculate the delta.
1323 if (flags == AMOTION_EVENT_ACTION_MOVE)
1324 {
1325 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
1326 }
1327 else
1328 {
1329 CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
1330 }
1331
1332 // Map touch[0] as mouse input for convenience
1333 CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0];
1334 CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
1335
1336 return 0;
1337}
1338
1339// EOF
diff --git a/raylib/src/platforms/rcore_desktop_glfw.c b/raylib/src/platforms/rcore_desktop_glfw.c
new file mode 100644
index 0000000..baf2967
--- /dev/null
+++ b/raylib/src/platforms/rcore_desktop_glfw.c
@@ -0,0 +1,1933 @@
1/**********************************************************************************************
2*
3* rcore_desktop - Functions to manage window, graphics device and inputs
4*
5* PLATFORM: DESKTOP: GLFW
6* - Windows (Win32, Win64)
7* - Linux (X11/Wayland desktop mode)
8* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
9* - OSX/macOS (x64, arm64)
10*
11* LIMITATIONS:
12* - Limitation 01
13* - Limitation 02
14*
15* POSSIBLE IMPROVEMENTS:
16* - Improvement 01
17* - Improvement 02
18*
19* ADDITIONAL NOTES:
20* - TRACELOG() function is located in raylib [utils] module
21*
22* CONFIGURATION:
23* #define RCORE_PLATFORM_CUSTOM_FLAG
24* Custom flag for rcore on target platform -not used-
25*
26* DEPENDENCIES:
27* - rglfw: Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...)
28* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
29*
30*
31* LICENSE: zlib/libpng
32*
33* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
34*
35* This software is provided "as-is", without any express or implied warranty. In no event
36* will the authors be held liable for any damages arising from the use of this software.
37*
38* Permission is granted to anyone to use this software for any purpose, including commercial
39* applications, and to alter it and redistribute it freely, subject to the following restrictions:
40*
41* 1. The origin of this software must not be misrepresented; you must not claim that you
42* wrote the original software. If you use this software in a product, an acknowledgment
43* in the product documentation would be appreciated but is not required.
44*
45* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
46* as being the original software.
47*
48* 3. This notice may not be removed or altered from any source distribution.
49*
50**********************************************************************************************/
51
52#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3
53 // NOTE: Already provided by rlgl implementation (on glad.h)
54#include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management
55 // NOTE: GLFW3 already includes gl.h (OpenGL) headers
56
57// Support retrieving native window handlers
58#if defined(_WIN32)
59 typedef void *PVOID;
60 typedef PVOID HANDLE;
61 #include "../external/win32_clipboard.h"
62 typedef HANDLE HWND;
63 #define GLFW_EXPOSE_NATIVE_WIN32
64 #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h
65 #include "GLFW/glfw3native.h"
66
67 #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
68 // NOTE: Those functions require linking with winmm library
69 //#pragma warning(disable: 4273)
70 __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
71 //#pragma warning(default: 4273)
72 #endif
73#endif
74#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
75 #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
76
77 //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
78 //#define GLFW_EXPOSE_NATIVE_WAYLAND
79 #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window()
80#endif
81#if defined(__APPLE__)
82 #include <unistd.h> // Required for: usleep()
83
84 //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition
85 void *glfwGetCocoaWindow(GLFWwindow* handle);
86 #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow()
87#endif
88
89//----------------------------------------------------------------------------------
90// Types and Structures Definition
91//----------------------------------------------------------------------------------
92typedef struct {
93 GLFWwindow *handle; // GLFW window handle (graphic device)
94} PlatformData;
95
96//----------------------------------------------------------------------------------
97// Global Variables Definition
98//----------------------------------------------------------------------------------
99extern CoreData CORE; // Global CORE state context
100
101static PlatformData platform = { 0 }; // Platform specific data
102
103//----------------------------------------------------------------------------------
104// Module Internal Functions Declaration
105//----------------------------------------------------------------------------------
106int InitPlatform(void); // Initialize platform (graphics, inputs and more)
107void ClosePlatform(void); // Close platform
108
109// Error callback event
110static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
111
112// Window callbacks events
113static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized
114static void WindowPosCallback(GLFWwindow* window, int x, int y); // GLFW3 WindowPos Callback, runs when window is moved
115static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored
116static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized
117static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus
118static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
119static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale
120
121// Input callbacks events
122static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
123static void CharCallback(GLFWwindow *window, unsigned int codepoint); // GLFW3 Char Callback, runs on key pressed (get codepoint value)
124static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed
125static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move
126static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Scrolling Callback, runs on mouse wheel
127static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
128static void JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback
129
130//----------------------------------------------------------------------------------
131// Module Functions Declaration
132//----------------------------------------------------------------------------------
133// NOTE: Functions declaration is provided by raylib.h
134
135//----------------------------------------------------------------------------------
136// Module Functions Definition: Window and Graphics Device
137//----------------------------------------------------------------------------------
138
139// Check if application should close
140// NOTE: By default, if KEY_ESCAPE pressed or window close icon clicked
141bool WindowShouldClose(void)
142{
143 if (CORE.Window.ready) return CORE.Window.shouldClose;
144 else return true;
145}
146
147// Toggle fullscreen mode
148void ToggleFullscreen(void)
149{
150 if (!CORE.Window.fullscreen)
151 {
152 // Store previous window position (in case we exit fullscreen)
153 CORE.Window.previousPosition = CORE.Window.position;
154
155 int monitorCount = 0;
156 int monitorIndex = GetCurrentMonitor();
157 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
158
159 // Use current monitor, so we correctly get the display the window is on
160 GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL;
161
162 if (monitor == NULL)
163 {
164 TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor");
165
166 CORE.Window.fullscreen = false;
167 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
168
169 glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
170 }
171 else
172 {
173 CORE.Window.fullscreen = true;
174 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
175
176 glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
177 }
178
179 }
180 else
181 {
182 CORE.Window.fullscreen = false;
183 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
184
185 glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
186
187 // we update the window position right away
188 CORE.Window.position.x = CORE.Window.previousPosition.x;
189 CORE.Window.position.y = CORE.Window.previousPosition.y;
190 }
191
192 // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
193 // NOTE: V-Sync can be enabled by graphic driver configuration
194 if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1);
195}
196
197// Toggle borderless windowed mode
198void ToggleBorderlessWindowed(void)
199{
200 // Leave fullscreen before attempting to set borderless windowed mode
201 bool wasOnFullscreen = false;
202 if (CORE.Window.fullscreen)
203 {
204 // fullscreen already saves the previous position so it does not need to be set here again
205 ToggleFullscreen();
206 wasOnFullscreen = true;
207 }
208
209 const int monitor = GetCurrentMonitor();
210 int monitorCount;
211 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
212
213 if ((monitor >= 0) && (monitor < monitorCount))
214 {
215 const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
216
217 if (mode)
218 {
219 if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE))
220 {
221 // Store screen position and size
222 // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here
223 if (!wasOnFullscreen) CORE.Window.previousPosition = CORE.Window.position;
224 CORE.Window.previousScreen = CORE.Window.screen;
225
226 // Set undecorated and topmost modes and flags
227 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
228 CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
229 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE);
230 CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
231
232 // Get monitor position and size
233 int monitorPosX = 0;
234 int monitorPosY = 0;
235 glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY);
236 const int monitorWidth = mode->width;
237 const int monitorHeight = mode->height;
238
239 // Set screen position and size
240 glfwSetWindowPos(platform.handle, monitorPosX, monitorPosY);
241 glfwSetWindowSize(platform.handle, monitorWidth, monitorHeight);
242
243 // Refocus window
244 glfwFocusWindow(platform.handle);
245
246 CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
247 }
248 else
249 {
250 // Remove topmost and undecorated modes and flags
251 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE);
252 CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
253 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
254 CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
255
256 // Return previous screen size and position
257 // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly
258 glfwSetWindowSize(platform.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height);
259 glfwSetWindowPos(platform.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y);
260
261 // Refocus window
262 glfwFocusWindow(platform.handle);
263
264 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
265
266 CORE.Window.position.x = CORE.Window.previousPosition.x;
267 CORE.Window.position.y = CORE.Window.previousPosition.y;
268 }
269 }
270 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
271 }
272 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
273}
274
275// Set window state: maximized, if resizable
276void MaximizeWindow(void)
277{
278 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
279 {
280 glfwMaximizeWindow(platform.handle);
281 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
282 }
283}
284
285// Set window state: minimized
286void MinimizeWindow(void)
287{
288 // NOTE: Following function launches callback that sets appropriate flag!
289 glfwIconifyWindow(platform.handle);
290}
291
292// Set window state: not minimized/maximized
293void RestoreWindow(void)
294{
295 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
296 {
297 // Restores the specified window if it was previously iconified (minimized) or maximized
298 glfwRestoreWindow(platform.handle);
299 CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
300 CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
301 }
302}
303
304// Set window configuration state using flags
305void SetWindowState(unsigned int flags)
306{
307 // Check previous state and requested state to apply required changes
308 // NOTE: In most cases the functions already change the flags internally
309
310 // State change: FLAG_VSYNC_HINT
311 if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0))
312 {
313 glfwSwapInterval(1);
314 CORE.Window.flags |= FLAG_VSYNC_HINT;
315 }
316
317 // State change: FLAG_BORDERLESS_WINDOWED_MODE
318 // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running
319 if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0))
320 {
321 ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function
322 }
323
324 // State change: FLAG_FULLSCREEN_MODE
325 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE))
326 {
327 ToggleFullscreen(); // NOTE: Window state flag updated inside function
328 }
329
330 // State change: FLAG_WINDOW_RESIZABLE
331 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
332 {
333 glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
334 CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
335 }
336
337 // State change: FLAG_WINDOW_UNDECORATED
338 if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED))
339 {
340 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
341 CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
342 }
343
344 // State change: FLAG_WINDOW_HIDDEN
345 if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
346 {
347 glfwHideWindow(platform.handle);
348 CORE.Window.flags |= FLAG_WINDOW_HIDDEN;
349 }
350
351 // State change: FLAG_WINDOW_MINIMIZED
352 if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
353 {
354 //GLFW_ICONIFIED
355 MinimizeWindow(); // NOTE: Window state flag updated inside function
356 }
357
358 // State change: FLAG_WINDOW_MAXIMIZED
359 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
360 {
361 //GLFW_MAXIMIZED
362 MaximizeWindow(); // NOTE: Window state flag updated inside function
363 }
364
365 // State change: FLAG_WINDOW_UNFOCUSED
366 if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
367 {
368 glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE);
369 CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
370 }
371
372 // State change: FLAG_WINDOW_TOPMOST
373 if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
374 {
375 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE);
376 CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
377 }
378
379 // State change: FLAG_WINDOW_ALWAYS_RUN
380 if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
381 {
382 CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
383 }
384
385 // The following states can not be changed after window creation
386
387 // State change: FLAG_WINDOW_TRANSPARENT
388 if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
389 {
390 TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
391 }
392
393 // State change: FLAG_WINDOW_HIGHDPI
394 if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
395 {
396 TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
397 }
398
399 // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
400 if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
401 {
402 glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
403 CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH;
404 }
405
406 // State change: FLAG_MSAA_4X_HINT
407 if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
408 {
409 TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
410 }
411
412 // State change: FLAG_INTERLACED_HINT
413 if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
414 {
415 TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
416 }
417}
418
419// Clear window configuration state flags
420void ClearWindowState(unsigned int flags)
421{
422 // Check previous state and requested state to apply required changes
423 // NOTE: In most cases the functions already change the flags internally
424
425 // State change: FLAG_VSYNC_HINT
426 if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0))
427 {
428 glfwSwapInterval(0);
429 CORE.Window.flags &= ~FLAG_VSYNC_HINT;
430 }
431
432 // State change: FLAG_BORDERLESS_WINDOWED_MODE
433 // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running
434 if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0))
435 {
436 ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function
437 }
438
439 // State change: FLAG_FULLSCREEN_MODE
440 if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0))
441 {
442 ToggleFullscreen(); // NOTE: Window state flag updated inside function
443 }
444
445 // State change: FLAG_WINDOW_RESIZABLE
446 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
447 {
448 glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
449 CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
450 }
451
452 // State change: FLAG_WINDOW_HIDDEN
453 if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
454 {
455 glfwShowWindow(platform.handle);
456 CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
457 }
458
459 // State change: FLAG_WINDOW_MINIMIZED
460 if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
461 {
462 RestoreWindow(); // NOTE: Window state flag updated inside function
463 }
464
465 // State change: FLAG_WINDOW_MAXIMIZED
466 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
467 {
468 RestoreWindow(); // NOTE: Window state flag updated inside function
469 }
470
471 // State change: FLAG_WINDOW_UNDECORATED
472 if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0))
473 {
474 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
475 CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
476 }
477
478 // State change: FLAG_WINDOW_UNFOCUSED
479 if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
480 {
481 glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE);
482 CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
483 }
484
485 // State change: FLAG_WINDOW_TOPMOST
486 if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
487 {
488 glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE);
489 CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
490 }
491
492 // State change: FLAG_WINDOW_ALWAYS_RUN
493 if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
494 {
495 CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
496 }
497
498 // The following states can not be changed after window creation
499
500 // State change: FLAG_WINDOW_TRANSPARENT
501 if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
502 {
503 TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
504 }
505
506 // State change: FLAG_WINDOW_HIGHDPI
507 if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
508 {
509 TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
510 }
511
512 // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
513 if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
514 {
515 glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
516 CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH;
517 }
518
519 // State change: FLAG_MSAA_4X_HINT
520 if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
521 {
522 TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
523 }
524
525 // State change: FLAG_INTERLACED_HINT
526 if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
527 {
528 TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
529 }
530}
531
532// Set icon for window
533// NOTE 1: Image must be in RGBA format, 8bit per channel
534// NOTE 2: Image is scaled by the OS for all required sizes
535void SetWindowIcon(Image image)
536{
537 if (image.data == NULL)
538 {
539 // Revert to the default window icon, pass in an empty image array
540 glfwSetWindowIcon(platform.handle, 0, NULL);
541 }
542 else
543 {
544 if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
545 {
546 GLFWimage icon[1] = { 0 };
547
548 icon[0].width = image.width;
549 icon[0].height = image.height;
550 icon[0].pixels = (unsigned char *)image.data;
551
552 // NOTE 1: We only support one image icon
553 // NOTE 2: The specified image data is copied before this function returns
554 glfwSetWindowIcon(platform.handle, 1, icon);
555 }
556 else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
557 }
558}
559
560// Set icon for window, multiple images
561// NOTE 1: Images must be in RGBA format, 8bit per channel
562// NOTE 2: The multiple images are used depending on provided sizes
563// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16
564void SetWindowIcons(Image *images, int count)
565{
566 if ((images == NULL) || (count <= 0))
567 {
568 // Revert to the default window icon, pass in an empty image array
569 glfwSetWindowIcon(platform.handle, 0, NULL);
570 }
571 else
572 {
573 int valid = 0;
574 GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage));
575
576 for (int i = 0; i < count; i++)
577 {
578 if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
579 {
580 icons[valid].width = images[i].width;
581 icons[valid].height = images[i].height;
582 icons[valid].pixels = (unsigned char *)images[i].data;
583
584 valid++;
585 }
586 else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
587 }
588 // NOTE: Images data is copied internally before this function returns
589 glfwSetWindowIcon(platform.handle, valid, icons);
590
591 RL_FREE(icons);
592 }
593}
594
595// Set title for window
596void SetWindowTitle(const char *title)
597{
598 CORE.Window.title = title;
599 glfwSetWindowTitle(platform.handle, title);
600}
601
602// Set window position on screen (windowed mode)
603void SetWindowPosition(int x, int y)
604{
605 // Update CORE.Window.position as well
606 CORE.Window.position.x = x;
607 CORE.Window.position.y = y;
608 glfwSetWindowPos(platform.handle, x, y);
609}
610
611// Set monitor for the current window
612void SetWindowMonitor(int monitor)
613{
614 int monitorCount = 0;
615 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
616
617 if ((monitor >= 0) && (monitor < monitorCount))
618 {
619 if (CORE.Window.fullscreen)
620 {
621 TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
622
623 const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
624 glfwSetWindowMonitor(platform.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate);
625 }
626 else
627 {
628 TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
629
630 // Here the render width has to be used again in case high dpi flag is enabled
631 const int screenWidth = CORE.Window.render.width;
632 const int screenHeight = CORE.Window.render.height;
633 int monitorWorkareaX = 0;
634 int monitorWorkareaY = 0;
635 int monitorWorkareaWidth = 0;
636 int monitorWorkareaHeight = 0;
637 glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight);
638
639 // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it
640 if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(platform.handle, monitorWorkareaX, monitorWorkareaY);
641 else
642 {
643 const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2);
644 const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2);
645 glfwSetWindowPos(platform.handle, x, y);
646 }
647 }
648 }
649 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
650}
651
652// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
653void SetWindowMinSize(int width, int height)
654{
655 CORE.Window.screenMin.width = width;
656 CORE.Window.screenMin.height = height;
657
658 int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width;
659 int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height;
660 int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width;
661 int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height;
662
663 glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight);
664}
665
666// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
667void SetWindowMaxSize(int width, int height)
668{
669 CORE.Window.screenMax.width = width;
670 CORE.Window.screenMax.height = height;
671
672 int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width;
673 int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height;
674 int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width;
675 int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height;
676
677 glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight);
678}
679
680// Set window dimensions
681void SetWindowSize(int width, int height)
682{
683 CORE.Window.screen.width = width;
684 CORE.Window.screen.height = height;
685
686 glfwSetWindowSize(platform.handle, width, height);
687}
688
689// Set window opacity, value opacity is between 0.0 and 1.0
690void SetWindowOpacity(float opacity)
691{
692 if (opacity >= 1.0f) opacity = 1.0f;
693 else if (opacity <= 0.0f) opacity = 0.0f;
694 glfwSetWindowOpacity(platform.handle, opacity);
695}
696
697// Set window focused
698void SetWindowFocused(void)
699{
700 glfwFocusWindow(platform.handle);
701}
702
703// Get native window handle
704void *GetWindowHandle(void)
705{
706#if defined(_WIN32)
707 // NOTE: Returned handle is: void *HWND (windows.h)
708 return glfwGetWin32Window(platform.handle);
709#endif
710#if defined(__linux__)
711 // NOTE: Returned handle is: unsigned long Window (X.h)
712 // typedef unsigned long XID;
713 // typedef XID Window;
714 //unsigned long id = (unsigned long)glfwGetX11Window(platform.handle);
715 //return NULL; // TODO: Find a way to return value... cast to void *?
716 return (void *)platform.handle;
717#endif
718#if defined(__APPLE__)
719 // NOTE: Returned handle is: (objc_object *)
720 return (void *)glfwGetCocoaWindow(platform.handle);
721#endif
722
723 return NULL;
724}
725
726// Get number of monitors
727int GetMonitorCount(void)
728{
729 int monitorCount = 0;
730
731 glfwGetMonitors(&monitorCount);
732
733 return monitorCount;
734}
735
736// Get number of monitors
737int GetCurrentMonitor(void)
738{
739 int index = 0;
740 int monitorCount = 0;
741 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
742 GLFWmonitor *monitor = NULL;
743
744 if (monitorCount >= 1)
745 {
746 if (IsWindowFullscreen())
747 {
748 // Get the handle of the monitor that the specified window is in full screen on
749 monitor = glfwGetWindowMonitor(platform.handle);
750
751 for (int i = 0; i < monitorCount; i++)
752 {
753 if (monitors[i] == monitor)
754 {
755 index = i;
756 break;
757 }
758 }
759 }
760 else
761 {
762 // In case the window is between two monitors, we use below logic
763 // to try to detect the "current monitor" for that window, note that
764 // this is probably an overengineered solution for a very side case
765 // trying to match SDL behaviour
766
767 int closestDist = 0x7FFFFFFF;
768
769 // Window center position
770 int wcx = 0;
771 int wcy = 0;
772
773 glfwGetWindowPos(platform.handle, &wcx, &wcy);
774 wcx += (int)CORE.Window.screen.width/2;
775 wcy += (int)CORE.Window.screen.height/2;
776
777 for (int i = 0; i < monitorCount; i++)
778 {
779 // Monitor top-left position
780 int mx = 0;
781 int my = 0;
782
783 monitor = monitors[i];
784 glfwGetMonitorPos(monitor, &mx, &my);
785 const GLFWvidmode *mode = glfwGetVideoMode(monitor);
786
787 if (mode)
788 {
789 const int right = mx + mode->width - 1;
790 const int bottom = my + mode->height - 1;
791
792 if ((wcx >= mx) &&
793 (wcx <= right) &&
794 (wcy >= my) &&
795 (wcy <= bottom))
796 {
797 index = i;
798 break;
799 }
800
801 int xclosest = wcx;
802 if (wcx < mx) xclosest = mx;
803 else if (wcx > right) xclosest = right;
804
805 int yclosest = wcy;
806 if (wcy < my) yclosest = my;
807 else if (wcy > bottom) yclosest = bottom;
808
809 int dx = wcx - xclosest;
810 int dy = wcy - yclosest;
811 int dist = (dx*dx) + (dy*dy);
812 if (dist < closestDist)
813 {
814 index = i;
815 closestDist = dist;
816 }
817 }
818 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
819 }
820 }
821 }
822
823 return index;
824}
825
826// Get selected monitor position
827Vector2 GetMonitorPosition(int monitor)
828{
829 int monitorCount = 0;
830 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
831
832 if ((monitor >= 0) && (monitor < monitorCount))
833 {
834 int x, y;
835 glfwGetMonitorPos(monitors[monitor], &x, &y);
836
837 return (Vector2){ (float)x, (float)y };
838 }
839 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
840 return (Vector2){ 0, 0 };
841}
842
843// Get selected monitor width (currently used by monitor)
844int GetMonitorWidth(int monitor)
845{
846 int width = 0;
847 int monitorCount = 0;
848 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
849
850 if ((monitor >= 0) && (monitor < monitorCount))
851 {
852 const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
853
854 if (mode) width = mode->width;
855 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
856 }
857 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
858
859 return width;
860}
861
862// Get selected monitor height (currently used by monitor)
863int GetMonitorHeight(int monitor)
864{
865 int height = 0;
866 int monitorCount = 0;
867 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
868
869 if ((monitor >= 0) && (monitor < monitorCount))
870 {
871 const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
872
873 if (mode) height = mode->height;
874 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
875 }
876 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
877
878 return height;
879}
880
881// Get selected monitor physical width in millimetres
882int GetMonitorPhysicalWidth(int monitor)
883{
884 int width = 0;
885 int monitorCount = 0;
886 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
887
888 if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], &width, NULL);
889 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
890
891 return width;
892}
893
894// Get selected monitor physical height in millimetres
895int GetMonitorPhysicalHeight(int monitor)
896{
897 int height = 0;
898 int monitorCount = 0;
899 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
900
901 if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &height);
902 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
903
904 return height;
905}
906
907// Get selected monitor refresh rate
908int GetMonitorRefreshRate(int monitor)
909{
910 int refresh = 0;
911 int monitorCount = 0;
912 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
913
914 if ((monitor >= 0) && (monitor < monitorCount))
915 {
916 const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]);
917 refresh = vidmode->refreshRate;
918 }
919 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
920
921 return refresh;
922}
923
924// Get the human-readable, UTF-8 encoded name of the selected monitor
925const char *GetMonitorName(int monitor)
926{
927 int monitorCount = 0;
928 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
929
930 if ((monitor >= 0) && (monitor < monitorCount))
931 {
932 return glfwGetMonitorName(monitors[monitor]);
933 }
934 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
935 return "";
936}
937
938// Get window position XY on monitor
939Vector2 GetWindowPosition(void)
940{
941 int x = 0;
942 int y = 0;
943
944 glfwGetWindowPos(platform.handle, &x, &y);
945
946 return (Vector2){ (float)x, (float)y };
947}
948
949// Get window scale DPI factor for current monitor
950Vector2 GetWindowScaleDPI(void)
951{
952 Vector2 scale = {0};
953 glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y);
954 return scale;
955}
956
957// Set clipboard text content
958void SetClipboardText(const char *text)
959{
960 glfwSetClipboardString(platform.handle, text);
961}
962
963// Get clipboard text content
964// NOTE: returned string is allocated and freed by GLFW
965const char *GetClipboardText(void)
966{
967 return glfwGetClipboardString(platform.handle);
968}
969
970#if defined(SUPPORT_CLIPBOARD_IMAGE)
971// Get clipboard image
972Image GetClipboardImage(void)
973{
974 Image image = {0};
975 unsigned long long int dataSize = 0;
976 void* fileData = NULL;
977
978#ifdef _WIN32
979 int width, height;
980 fileData = (void*)Win32GetClipboardImageData(&width, &height, &dataSize);
981#else
982 TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_GLFW doesn't implement `GetClipboardImage` for this OS");
983#endif
984
985 if (fileData == NULL)
986 {
987 TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data.");
988 }
989 else
990 {
991 image = LoadImageFromMemory(".bmp", fileData, (int)dataSize);
992 }
993 return image;
994}
995#endif // SUPPORT_CLIPBOARD_IMAGE
996
997// Show mouse cursor
998void ShowCursor(void)
999{
1000 glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
1001 CORE.Input.Mouse.cursorHidden = false;
1002}
1003
1004// Hides mouse cursor
1005void HideCursor(void)
1006{
1007 glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
1008 CORE.Input.Mouse.cursorHidden = true;
1009}
1010
1011// Enables cursor (unlock cursor)
1012void EnableCursor(void)
1013{
1014 glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
1015
1016 // Set cursor position in the middle
1017 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
1018
1019 if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
1020
1021 CORE.Input.Mouse.cursorHidden = false;
1022}
1023
1024// Disables cursor (lock cursor)
1025void DisableCursor(void)
1026{
1027 glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
1028
1029 // Set cursor position in the middle
1030 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
1031
1032 if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
1033
1034 CORE.Input.Mouse.cursorHidden = true;
1035}
1036
1037// Swap back buffer with front buffer (screen drawing)
1038void SwapScreenBuffer(void)
1039{
1040 glfwSwapBuffers(platform.handle);
1041}
1042
1043//----------------------------------------------------------------------------------
1044// Module Functions Definition: Misc
1045//----------------------------------------------------------------------------------
1046
1047// Get elapsed time measure in seconds since InitTimer()
1048double GetTime(void)
1049{
1050 double time = glfwGetTime(); // Elapsed time since glfwInit()
1051 return time;
1052}
1053
1054// Open URL with default system browser (if available)
1055// NOTE: This function is only safe to use if you control the URL given.
1056// A user could craft a malicious string performing another action.
1057// Only call this function yourself not with user input or make sure to check the string yourself.
1058// Ref: https://github.com/raysan5/raylib/issues/686
1059void OpenURL(const char *url)
1060{
1061 // Security check to (partially) avoid malicious code
1062 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
1063 else
1064 {
1065 char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
1066#if defined(_WIN32)
1067 sprintf(cmd, "explorer \"%s\"", url);
1068#endif
1069#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
1070 sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
1071#endif
1072#if defined(__APPLE__)
1073 sprintf(cmd, "open '%s'", url);
1074#endif
1075 int result = system(cmd);
1076 if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
1077 RL_FREE(cmd);
1078 }
1079}
1080
1081//----------------------------------------------------------------------------------
1082// Module Functions Definition: Inputs
1083//----------------------------------------------------------------------------------
1084
1085// Set internal gamepad mappings
1086int SetGamepadMappings(const char *mappings)
1087{
1088 return glfwUpdateGamepadMappings(mappings);
1089}
1090
1091// Set gamepad vibration
1092void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
1093{
1094 TRACELOG(LOG_WARNING, "GamepadSetVibration() not available on target platform");
1095}
1096
1097// Set mouse position XY
1098void SetMousePosition(int x, int y)
1099{
1100 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
1101 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
1102
1103 // NOTE: emscripten not implemented
1104 glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
1105}
1106
1107// Set mouse cursor
1108void SetMouseCursor(int cursor)
1109{
1110 CORE.Input.Mouse.cursor = cursor;
1111 if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(platform.handle, NULL);
1112 else
1113 {
1114 // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values
1115 glfwSetCursor(platform.handle, glfwCreateStandardCursor(0x00036000 + cursor));
1116 }
1117}
1118
1119// Get physical key name.
1120const char *GetKeyName(int key)
1121{
1122 return glfwGetKeyName(key, glfwGetKeyScancode(key));
1123}
1124
1125// Register all input events
1126void PollInputEvents(void)
1127{
1128#if defined(SUPPORT_GESTURES_SYSTEM)
1129 // NOTE: Gestures update must be called every frame to reset gestures correctly
1130 // because ProcessGestureEvent() is just called on an event, not every frame
1131 UpdateGestures();
1132#endif
1133
1134 // Reset keys/chars pressed registered
1135 CORE.Input.Keyboard.keyPressedQueueCount = 0;
1136 CORE.Input.Keyboard.charPressedQueueCount = 0;
1137
1138 // Reset last gamepad button/axis registered state
1139 CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
1140 //CORE.Input.Gamepad.axisCount = 0;
1141
1142 // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback)
1143
1144 // Register previous keys states
1145 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
1146 {
1147 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
1148 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
1149 }
1150
1151 // Register previous mouse states
1152 for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
1153
1154 // Register previous mouse wheel state
1155 CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
1156 CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
1157
1158 // Register previous mouse position
1159 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
1160
1161 // Register previous touch states
1162 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
1163
1164 // Reset touch positions
1165 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
1166
1167 // Map touch position to mouse position for convenience
1168 // WARNING: If the target desktop device supports touch screen, this behaviour should be reviewed!
1169 // TODO: GLFW does not support multi-touch input just yet
1170 // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
1171 // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
1172 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
1173
1174 // Check if gamepads are ready
1175 // NOTE: We do it here in case of disconnection
1176 for (int i = 0; i < MAX_GAMEPADS; i++)
1177 {
1178 if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true;
1179 else CORE.Input.Gamepad.ready[i] = false;
1180 }
1181
1182 // Register gamepads buttons events
1183 for (int i = 0; i < MAX_GAMEPADS; i++)
1184 {
1185 if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available
1186 {
1187 // Register previous gamepad states
1188 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
1189
1190 // Get current gamepad state
1191 // NOTE: There is no callback available, so we get it manually
1192 GLFWgamepadstate state = { 0 };
1193 glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller
1194
1195 const unsigned char *buttons = state.buttons;
1196
1197 for (int k = 0; (buttons != NULL) && (k < MAX_GAMEPAD_BUTTONS); k++)
1198 {
1199 int button = -1; // GamepadButton enum values assigned
1200
1201 switch (k)
1202 {
1203 case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
1204 case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
1205 case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
1206 case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
1207
1208 case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
1209 case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
1210
1211 case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
1212 case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
1213 case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
1214
1215 case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
1216 case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
1217 case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
1218 case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
1219
1220 case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
1221 case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
1222 default: break;
1223 }
1224
1225 if (button != -1) // Check for valid button
1226 {
1227 if (buttons[k] == GLFW_PRESS)
1228 {
1229 CORE.Input.Gamepad.currentButtonState[i][button] = 1;
1230 CORE.Input.Gamepad.lastButtonPressed = button;
1231 }
1232 else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
1233 }
1234 }
1235
1236 // Get current axis state
1237 const float *axes = state.axes;
1238
1239 for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1); k++)
1240 {
1241 CORE.Input.Gamepad.axisState[i][k] = axes[k];
1242 }
1243
1244 // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis)
1245 CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f);
1246 CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f);
1247
1248 CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1;
1249 }
1250 }
1251
1252 CORE.Window.resizedLastFrame = false;
1253
1254 if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused)
1255 else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) -> Update keys state
1256
1257 // While window minimized, stop loop execution
1258 while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents();
1259
1260 CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle);
1261
1262 // Reset close status for next frame
1263 glfwSetWindowShouldClose(platform.handle, GLFW_FALSE);
1264}
1265
1266//----------------------------------------------------------------------------------
1267// Module Internal Functions Definition
1268//----------------------------------------------------------------------------------
1269
1270static void SetDimensionsFromMonitor(GLFWmonitor *monitor)
1271{
1272 const GLFWvidmode *mode = glfwGetVideoMode(monitor);
1273
1274 // Default display resolution to that of the current mode
1275 CORE.Window.display.width = mode->width;
1276 CORE.Window.display.height = mode->height;
1277
1278 // Set screen width/height to the display width/height if they are 0
1279 if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
1280 if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
1281}
1282
1283// Initialize platform: graphics, inputs and more
1284int InitPlatform(void)
1285{
1286 glfwSetErrorCallback(ErrorCallback);
1287/*
1288 // TODO: Setup GLFW custom allocators to match raylib ones
1289 const GLFWallocator allocator = {
1290 .allocate = MemAlloc,
1291 .deallocate = MemFree,
1292 .reallocate = MemRealloc,
1293 .user = NULL
1294 };
1295
1296 glfwInitAllocator(&allocator);
1297*/
1298
1299#if defined(__APPLE__)
1300 glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
1301#endif
1302 // Initialize GLFW internal global state
1303 int result = glfwInit();
1304 if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; }
1305
1306 // Initialize graphic device: display/window and graphic context
1307 //----------------------------------------------------------------------------
1308 glfwDefaultWindowHints(); // Set default windows hints
1309 //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits
1310 //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits
1311 //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits
1312 //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits
1313 //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits
1314 //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window
1315 //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
1316 //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
1317
1318 // Disable GlFW auto iconify behaviour
1319 // Auto Iconify automatically minimizes (iconifies) the window if the window loses focus
1320 // additionally auto iconify restores the hardware resolution of the monitor if the window that loses focus is a fullscreen window
1321 glfwWindowHint(GLFW_AUTO_ICONIFY, 0);
1322
1323 // Check window creation flags
1324 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
1325
1326 if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
1327 else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden
1328
1329 if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
1330 else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window
1331
1332 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
1333 else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable
1334
1335 // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
1336 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
1337
1338 // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
1339 if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
1340
1341 if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
1342 else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
1343
1344 if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
1345 else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
1346
1347 // NOTE: Some GLFW flags are not supported on HTML5
1348 if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer
1349 else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer
1350
1351 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
1352 {
1353 // Resize window content area based on the monitor content scale.
1354 // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11.
1355 // On platforms like macOS the resolution of the framebuffer is changed independently of the window size.
1356 glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on
1357#if defined(__APPLE__)
1358 glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
1359#endif
1360 }
1361 else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
1362
1363 // Mouse passthrough
1364 if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
1365 else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
1366
1367 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
1368 {
1369 // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
1370 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
1371 glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0
1372 }
1373
1374 // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version
1375 // with backward compatibility to older OpenGL versions.
1376 // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context.
1377
1378 // Check selection OpenGL version
1379 if (rlGetVersion() == RL_OPENGL_21)
1380 {
1381 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint)
1382 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint)
1383 }
1384 else if (rlGetVersion() == RL_OPENGL_33)
1385 {
1386 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
1387 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
1388 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
1389 // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
1390#if defined(__APPLE__)
1391 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility
1392#else
1393 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above!
1394#endif
1395 //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
1396 }
1397 else if (rlGetVersion() == RL_OPENGL_43)
1398 {
1399 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint)
1400 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
1401 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
1402 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
1403#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
1404 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context
1405#endif
1406 }
1407 else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context
1408 {
1409 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
1410 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
1411 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
1412 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
1413 }
1414 else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context
1415 {
1416 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
1417 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
1418 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
1419 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
1420 }
1421
1422 // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions.
1423 // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn.
1424 // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience.
1425 // REF: https://github.com/raysan5/raylib/issues/1554
1426 glfwSetJoystickCallback(NULL);
1427
1428 GLFWmonitor *monitor = NULL;
1429 if (CORE.Window.fullscreen)
1430 {
1431 // According to glfwCreateWindow(), if the user does not have a choice, fullscreen applications
1432 // should default to the primary monitor.
1433
1434 monitor = glfwGetPrimaryMonitor();
1435 if (!monitor)
1436 {
1437 TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
1438 return -1;
1439 }
1440
1441 SetDimensionsFromMonitor(monitor);
1442
1443 // Remember center for switching from fullscreen to window
1444 if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
1445 {
1446 // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
1447 // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
1448 CORE.Window.position.x = CORE.Window.display.width/4;
1449 CORE.Window.position.y = CORE.Window.display.height/4;
1450 }
1451 else
1452 {
1453 CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
1454 CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
1455 }
1456
1457 if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
1458 if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
1459
1460 // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor
1461 int count = 0;
1462 const GLFWvidmode *modes = glfwGetVideoModes(monitor, &count);
1463
1464 // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height
1465 for (int i = 0; i < count; i++)
1466 {
1467 if ((unsigned int)modes[i].width >= CORE.Window.screen.width)
1468 {
1469 if ((unsigned int)modes[i].height >= CORE.Window.screen.height)
1470 {
1471 CORE.Window.display.width = modes[i].width;
1472 CORE.Window.display.height = modes[i].height;
1473 break;
1474 }
1475 }
1476 }
1477
1478 TRACELOG(LOG_INFO, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1479
1480 // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
1481 // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
1482 // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
1483 // by the sides to fit all monitor space...
1484
1485 // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
1486 // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
1487 // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
1488 // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
1489 // HighDPI monitors are properly considered in a following similar function: SetupViewport()
1490 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
1491
1492 platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL);
1493
1494 // NOTE: Full-screen change, not working properly...
1495 //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
1496 }
1497 else
1498 {
1499 // No-fullscreen window creation
1500 bool requestWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0);
1501
1502 // Default to at least one pixel in size, as creation with a zero dimension is not allowed.
1503 int creationWidth = CORE.Window.screen.width != 0 ? CORE.Window.screen.width : 1;
1504 int creationHeight = CORE.Window.screen.height != 0 ? CORE.Window.screen.height : 1;
1505
1506 platform.handle = glfwCreateWindow(creationWidth, creationHeight, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
1507
1508 // After the window was created, determine the monitor that the window manager assigned.
1509 // Derive display sizes, and, if possible, window size in case it was zero at beginning.
1510
1511 int monitorCount = 0;
1512 int monitorIndex = GetCurrentMonitor();
1513 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1514
1515 if (monitorIndex < monitorCount)
1516 {
1517 monitor = monitors[monitorIndex];
1518 SetDimensionsFromMonitor(monitor);
1519
1520 if (requestWindowedFullscreen) glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height);
1521 }
1522 else
1523 {
1524 // The monitor for the window-manager-created window can not be determined, so it can not be centered.
1525 glfwTerminate();
1526 TRACELOG(LOG_WARNING, "GLFW: Failed to determine Monitor to center Window");
1527 return -1;
1528 }
1529
1530 if (platform.handle)
1531 {
1532 CORE.Window.render.width = CORE.Window.screen.width;
1533 CORE.Window.render.height = CORE.Window.screen.height;
1534 }
1535 }
1536
1537 if (!platform.handle)
1538 {
1539 glfwTerminate();
1540 TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
1541 return -1;
1542 }
1543
1544 glfwMakeContextCurrent(platform.handle);
1545 result = glfwGetError(NULL);
1546
1547 // Check context activation
1548 if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR))
1549 {
1550 CORE.Window.ready = true;
1551
1552 glfwSwapInterval(0); // No V-Sync by default
1553
1554 // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
1555 // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need
1556 // to be activated on web platforms since VSync is enforced there.
1557 if (CORE.Window.flags & FLAG_VSYNC_HINT)
1558 {
1559 // WARNING: It seems to hit a critical render path in Intel HD Graphics
1560 glfwSwapInterval(1);
1561 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC");
1562 }
1563
1564 int fbWidth = CORE.Window.screen.width;
1565 int fbHeight = CORE.Window.screen.height;
1566
1567 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
1568 {
1569 // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling.
1570 // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
1571 #if !defined(__APPLE__)
1572 glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight);
1573
1574 // Screen scaling matrix is required in case desired screen area is different from display area
1575 CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
1576
1577 // Mouse input scaling for the new screen size
1578 SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight);
1579 #endif
1580 }
1581
1582 CORE.Window.render.width = fbWidth;
1583 CORE.Window.render.height = fbHeight;
1584 CORE.Window.currentFbo.width = fbWidth;
1585 CORE.Window.currentFbo.height = fbHeight;
1586
1587 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
1588 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1589 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
1590 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
1591 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
1592 }
1593 else
1594 {
1595 TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
1596 return -1;
1597 }
1598
1599 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
1600
1601 // If graphic device is no properly initialized, we end program
1602 if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
1603 else
1604 {
1605 // Try to center window on screen but avoiding window-bar outside of screen
1606 int monitorX = 0;
1607 int monitorY = 0;
1608 int monitorWidth = 0;
1609 int monitorHeight = 0;
1610 glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight);
1611
1612 // Here CORE.Window.render.width/height should be used instead of CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled.
1613 int posX = monitorX + (monitorWidth - (int)CORE.Window.render.width)/2;
1614 int posY = monitorY + (monitorHeight - (int)CORE.Window.render.height)/2;
1615 if (posX < monitorX) posX = monitorX;
1616 if (posY < monitorY) posY = monitorY;
1617 SetWindowPosition(posX, posY);
1618
1619 // Update CORE.Window.position here so it is correct from the start
1620 CORE.Window.position.x = posX;
1621 CORE.Window.position.y = posY;
1622 }
1623
1624 // Load OpenGL extensions
1625 // NOTE: GL procedures address loader is required to load extensions
1626 rlLoadExtensions(glfwGetProcAddress);
1627 //----------------------------------------------------------------------------
1628
1629 // Initialize input events callbacks
1630 //----------------------------------------------------------------------------
1631 // Set window callback events
1632 glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default!
1633 glfwSetWindowPosCallback(platform.handle, WindowPosCallback);
1634 glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback);
1635 glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
1636 glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
1637 glfwSetDropCallback(platform.handle, WindowDropCallback);
1638
1639 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
1640 {
1641 glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
1642 }
1643
1644 // Set input callback events
1645 glfwSetKeyCallback(platform.handle, KeyCallback);
1646 glfwSetCharCallback(platform.handle, CharCallback);
1647 glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
1648 glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes
1649 glfwSetScrollCallback(platform.handle, MouseScrollCallback);
1650 glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
1651 glfwSetJoystickCallback(JoystickCallback);
1652
1653 glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM)
1654
1655 // Retrieve gamepad names
1656 for (int i = 0; i < MAX_GAMEPADS; i++)
1657 {
1658 if (glfwJoystickPresent(i)) strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i));
1659 }
1660 //----------------------------------------------------------------------------
1661
1662 // Initialize timming system
1663 //----------------------------------------------------------------------------
1664 InitTimer();
1665 //----------------------------------------------------------------------------
1666
1667 // Initialize storage system
1668 //----------------------------------------------------------------------------
1669 CORE.Storage.basePath = GetWorkingDirectory();
1670 //----------------------------------------------------------------------------
1671
1672#if defined(__NetBSD__)
1673 // Workaround for NetBSD
1674 char *glfwPlatform = "X11";
1675#else
1676 char *glfwPlatform = "";
1677 switch (glfwGetPlatform())
1678 {
1679 case GLFW_PLATFORM_WIN32: glfwPlatform = "Win32"; break;
1680 case GLFW_PLATFORM_COCOA: glfwPlatform = "Cocoa"; break;
1681 case GLFW_PLATFORM_WAYLAND: glfwPlatform = "Wayland"; break;
1682 case GLFW_PLATFORM_X11: glfwPlatform = "X11"; break;
1683 case GLFW_PLATFORM_NULL: glfwPlatform = "Null"; break;
1684 default: break;
1685 }
1686#endif
1687
1688 TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW - %s): Initialized successfully", glfwPlatform);
1689
1690 return 0;
1691}
1692
1693// Close platform
1694void ClosePlatform(void)
1695{
1696 glfwDestroyWindow(platform.handle);
1697 glfwTerminate();
1698
1699#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
1700 timeEndPeriod(1); // Restore time period
1701#endif
1702}
1703
1704// GLFW3 Error Callback, runs on GLFW3 error
1705static void ErrorCallback(int error, const char *description)
1706{
1707 TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
1708}
1709
1710// GLFW3 WindowSize Callback, runs when window is resizedLastFrame
1711// NOTE: Window resizing not allowed by default
1712static void WindowSizeCallback(GLFWwindow *window, int width, int height)
1713{
1714 // Reset viewport and projection matrix for new size
1715 SetupViewport(width, height);
1716
1717 CORE.Window.currentFbo.width = width;
1718 CORE.Window.currentFbo.height = height;
1719 CORE.Window.resizedLastFrame = true;
1720
1721 if (IsWindowFullscreen()) return;
1722
1723 // Set current screen size
1724
1725 CORE.Window.screen.width = width;
1726 CORE.Window.screen.height = height;
1727
1728 // NOTE: Postprocessing texture is not scaled to new size
1729}
1730static void WindowPosCallback(GLFWwindow* window, int x, int y)
1731{
1732 // Set current window position
1733 CORE.Window.position.x = x;
1734 CORE.Window.position.y = y;
1735}
1736static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
1737{
1738 CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
1739}
1740
1741// GLFW3 WindowIconify Callback, runs when window is minimized/restored
1742static void WindowIconifyCallback(GLFWwindow *window, int iconified)
1743{
1744 if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
1745 else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
1746}
1747
1748// GLFW3 WindowMaximize Callback, runs when window is maximized/restored
1749static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
1750{
1751 if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized
1752 else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored
1753}
1754
1755// GLFW3 WindowFocus Callback, runs when window get/lose focus
1756static void WindowFocusCallback(GLFWwindow *window, int focused)
1757{
1758 if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused
1759 else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus
1760}
1761
1762// GLFW3 Window Drop Callback, runs when drop files into window
1763static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
1764{
1765 if (count > 0)
1766 {
1767 // In case previous dropped filepaths have not been freed, we free them
1768 if (CORE.Window.dropFileCount > 0)
1769 {
1770 for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]);
1771
1772 RL_FREE(CORE.Window.dropFilepaths);
1773
1774 CORE.Window.dropFileCount = 0;
1775 CORE.Window.dropFilepaths = NULL;
1776 }
1777
1778 // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy
1779 CORE.Window.dropFileCount = count;
1780 CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *));
1781
1782 for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
1783 {
1784 CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
1785 strcpy(CORE.Window.dropFilepaths[i], paths[i]);
1786 }
1787 }
1788}
1789
1790// GLFW3 Keyboard Callback, runs on key pressed
1791static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
1792{
1793 if (key < 0) return; // Security check, macOS fn key generates -1
1794
1795 // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
1796 // to work properly with our implementation (IsKeyDown/IsKeyUp checks)
1797 if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
1798 else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
1799 else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
1800
1801 // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys
1802 if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) ||
1803 ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1;
1804
1805 // Check if there is space available in the key queue
1806 if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS))
1807 {
1808 // Add character to the queue
1809 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
1810 CORE.Input.Keyboard.keyPressedQueueCount++;
1811 }
1812
1813 // Check the exit key to set close window
1814 if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
1815}
1816
1817// GLFW3 Char Callback, get unicode codepoint value
1818static void CharCallback(GLFWwindow *window, unsigned int codepoint)
1819{
1820 //TRACELOG(LOG_DEBUG, "Char Callback: Codepoint: %i", codepoint);
1821
1822 // NOTE: Registers any key down considering OS keyboard layout but
1823 // does not detect action events, those should be managed by user...
1824 // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
1825 // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
1826
1827 // Check if there is space available in the queue
1828 if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
1829 {
1830 // Add character to the queue
1831 CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint;
1832 CORE.Input.Keyboard.charPressedQueueCount++;
1833 }
1834}
1835
1836// GLFW3 Mouse Button Callback, runs on mouse button pressed
1837static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
1838{
1839 // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
1840 // but future releases may add more actions (i.e. GLFW_REPEAT)
1841 CORE.Input.Mouse.currentButtonState[button] = action;
1842 CORE.Input.Touch.currentTouchState[button] = action;
1843
1844#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
1845 // Process mouse events as touches to be able to use mouse-gestures
1846 GestureEvent gestureEvent = { 0 };
1847
1848 // Register touch actions
1849 if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
1850 else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP;
1851
1852 // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback()
1853
1854 // Assign a pointer ID
1855 gestureEvent.pointId[0] = 0;
1856
1857 // Register touch points count
1858 gestureEvent.pointCount = 1;
1859
1860 // Register touch points position, only one point registered
1861 gestureEvent.position[0] = GetMousePosition();
1862
1863 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
1864 gestureEvent.position[0].x /= (float)GetScreenWidth();
1865 gestureEvent.position[0].y /= (float)GetScreenHeight();
1866
1867 // Gesture data is sent to gestures-system for processing
1868 ProcessGestureEvent(gestureEvent);
1869#endif
1870}
1871
1872// GLFW3 Cursor Position Callback, runs on mouse move
1873static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
1874{
1875 CORE.Input.Mouse.currentPosition.x = (float)x;
1876 CORE.Input.Mouse.currentPosition.y = (float)y;
1877 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
1878
1879#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
1880 // Process mouse events as touches to be able to use mouse-gestures
1881 GestureEvent gestureEvent = { 0 };
1882
1883 gestureEvent.touchAction = TOUCH_ACTION_MOVE;
1884
1885 // Assign a pointer ID
1886 gestureEvent.pointId[0] = 0;
1887
1888 // Register touch points count
1889 gestureEvent.pointCount = 1;
1890
1891 // Register touch points position, only one point registered
1892 gestureEvent.position[0] = CORE.Input.Touch.position[0];
1893
1894 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
1895 gestureEvent.position[0].x /= (float)GetScreenWidth();
1896 gestureEvent.position[0].y /= (float)GetScreenHeight();
1897
1898 // Gesture data is sent to gestures-system for processing
1899 ProcessGestureEvent(gestureEvent);
1900#endif
1901}
1902
1903// GLFW3 Scrolling Callback, runs on mouse wheel
1904static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
1905{
1906 CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset };
1907}
1908
1909// GLFW3 CursorEnter Callback, when cursor enters the window
1910static void CursorEnterCallback(GLFWwindow *window, int enter)
1911{
1912 if (enter) CORE.Input.Mouse.cursorOnScreen = true;
1913 else CORE.Input.Mouse.cursorOnScreen = false;
1914}
1915
1916// GLFW3 Joystick Connected/Disconnected Callback
1917static void JoystickCallback(int jid, int event)
1918{
1919 if (event == GLFW_CONNECTED)
1920 {
1921 strcpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid));
1922 }
1923 else if (event == GLFW_DISCONNECTED)
1924 {
1925 memset(CORE.Input.Gamepad.name[jid], 0, 64);
1926 }
1927}
1928
1929#ifdef _WIN32
1930# define WIN32_CLIPBOARD_IMPLEMENTATION
1931# include "../external/win32_clipboard.h"
1932#endif
1933// EOF
diff --git a/raylib/src/platforms/rcore_desktop_rgfw.c b/raylib/src/platforms/rcore_desktop_rgfw.c
new file mode 100644
index 0000000..f3f26e3
--- /dev/null
+++ b/raylib/src/platforms/rcore_desktop_rgfw.c
@@ -0,0 +1,1387 @@
1/**********************************************************************************************
2*
3* rcore_desktop_rgfw - Functions to manage window, graphics device and inputs
4*
5* PLATFORM: RGFW
6* - Windows (Win32, Win64)
7* - Linux (X11/Wayland desktop mode)
8* - MacOS (Cocoa)
9*
10* LIMITATIONS:
11* - TODO
12*
13* POSSIBLE IMPROVEMENTS:
14* - TODO
15*
16* ADDITIONAL NOTES:
17* - TRACELOG() function is located in raylib [utils] module
18*
19* CONFIGURATION:
20* #define RCORE_PLATFORM_RGFW
21* Custom flag for rcore on target platform RGFW
22*
23* DEPENDENCIES:
24* - RGFW.h (main library): Windowing and inputs management
25* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
26*
27*
28* LICENSE: zlib/libpng
29*
30* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5), Colleague Riley and contributors
31*
32* This software is provided "as-is", without any express or implied warranty. In no event
33* will the authors be held liable for any damages arising from the use of this software.
34*
35* Permission is granted to anyone to use this software for any purpose, including commercial
36* applications, and to alter it and redistribute it freely, subject to the following restrictions:
37*
38* 1. The origin of this software must not be misrepresented; you must not claim that you
39* wrote the original software. If you use this software in a product, an acknowledgment
40* in the product documentation would be appreciated but is not required.
41*
42* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
43* as being the original software.
44*
45* 3. This notice may not be removed or altered from any source distribution.
46*
47**********************************************************************************************/
48
49#if defined(GRAPHICS_API_OPENGL_ES2)
50 #define RGFW_OPENGL_ES2
51#endif
52
53void ShowCursor(void);
54void CloseWindow(void);
55
56#if defined(__linux__)
57 #define _INPUT_EVENT_CODES_H
58#endif
59
60#if defined(__unix__) || defined(__linux__)
61 #define _XTYPEDEF_FONT
62#endif
63
64#define RGFW_IMPLEMENTATION
65
66#if defined(_WIN32) || defined(_WIN64)
67 #define WIN32_LEAN_AND_MEAN
68 #define Rectangle rectangle_win32
69 #define CloseWindow CloseWindow_win32
70 #define ShowCursor __imp_ShowCursor
71 #define _APISETSTRING_
72
73 #undef MAX_PATH
74
75 __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
76#endif
77
78#if defined(__APPLE__)
79 #define Point NSPOINT
80 #define Size NSSIZE
81#endif
82
83#include "../external/RGFW.h"
84
85#if defined(_WIN32) || defined(_WIN64)
86 #undef DrawText
87 #undef ShowCursor
88 #undef CloseWindow
89 #undef Rectangle
90
91 #undef MAX_PATH
92 #define MAX_PATH 1025
93#endif
94
95#if defined(__APPLE__)
96 #undef Point
97 #undef Size
98#endif
99
100#include <stdbool.h>
101#include <string.h> // Required for: strcmp()
102
103//----------------------------------------------------------------------------------
104// Types and Structures Definition
105//----------------------------------------------------------------------------------
106typedef struct {
107 RGFW_window *window; // Native display device (physical screen connection)
108} PlatformData;
109
110//----------------------------------------------------------------------------------
111// Global Variables Definition
112//----------------------------------------------------------------------------------
113extern CoreData CORE; // Global CORE state context
114
115static PlatformData platform = { NULL }; // Platform specific
116
117static bool RGFW_disableCursor = false;
118
119static const unsigned short keyMappingRGFW[] = {
120 [RGFW_KEY_NULL] = KEY_NULL,
121 [RGFW_Quote] = KEY_APOSTROPHE,
122 [RGFW_Comma] = KEY_COMMA,
123 [RGFW_Minus] = KEY_MINUS,
124 [RGFW_Period] = KEY_PERIOD,
125 [RGFW_Slash] = KEY_SLASH,
126 [RGFW_Escape] = KEY_ESCAPE,
127 [RGFW_F1] = KEY_F1,
128 [RGFW_F2] = KEY_F2,
129 [RGFW_F3] = KEY_F3,
130 [RGFW_F4] = KEY_F4,
131 [RGFW_F5] = KEY_F5,
132 [RGFW_F6] = KEY_F6,
133 [RGFW_F7] = KEY_F7,
134 [RGFW_F8] = KEY_F8,
135 [RGFW_F9] = KEY_F9,
136 [RGFW_F10] = KEY_F10,
137 [RGFW_F11] = KEY_F11,
138 [RGFW_F12] = KEY_F12,
139 [RGFW_Backtick] = KEY_GRAVE,
140 [RGFW_0] = KEY_ZERO,
141 [RGFW_1] = KEY_ONE,
142 [RGFW_2] = KEY_TWO,
143 [RGFW_3] = KEY_THREE,
144 [RGFW_4] = KEY_FOUR,
145 [RGFW_5] = KEY_FIVE,
146 [RGFW_6] = KEY_SIX,
147 [RGFW_7] = KEY_SEVEN,
148 [RGFW_8] = KEY_EIGHT,
149 [RGFW_9] = KEY_NINE,
150 [RGFW_Equals] = KEY_EQUAL,
151 [RGFW_BackSpace] = KEY_BACKSPACE,
152 [RGFW_Tab] = KEY_TAB,
153 [RGFW_CapsLock] = KEY_CAPS_LOCK,
154 [RGFW_ShiftL] = KEY_LEFT_SHIFT,
155 [RGFW_ControlL] = KEY_LEFT_CONTROL,
156 [RGFW_AltL] = KEY_LEFT_ALT,
157 [RGFW_SuperL] = KEY_LEFT_SUPER,
158 #ifndef RGFW_MACOS
159 [RGFW_ShiftR] = KEY_RIGHT_SHIFT,
160
161 [RGFW_AltR] = KEY_RIGHT_ALT,
162 #endif
163 [RGFW_Space] = KEY_SPACE,
164
165 [RGFW_a] = KEY_A,
166 [RGFW_b] = KEY_B,
167 [RGFW_c] = KEY_C,
168 [RGFW_d] = KEY_D,
169 [RGFW_e] = KEY_E,
170 [RGFW_f] = KEY_F,
171 [RGFW_g] = KEY_G,
172 [RGFW_h] = KEY_H,
173 [RGFW_i] = KEY_I,
174 [RGFW_j] = KEY_J,
175 [RGFW_k] = KEY_K,
176 [RGFW_l] = KEY_L,
177 [RGFW_m] = KEY_M,
178 [RGFW_n] = KEY_N,
179 [RGFW_o] = KEY_O,
180 [RGFW_p] = KEY_P,
181 [RGFW_q] = KEY_Q,
182 [RGFW_r] = KEY_R,
183 [RGFW_s] = KEY_S,
184 [RGFW_t] = KEY_T,
185 [RGFW_u] = KEY_U,
186 [RGFW_v] = KEY_V,
187 [RGFW_w] = KEY_W,
188 [RGFW_x] = KEY_X,
189 [RGFW_y] = KEY_Y,
190 [RGFW_z] = KEY_Z,
191 [RGFW_Bracket] = KEY_LEFT_BRACKET,
192 [RGFW_BackSlash] = KEY_BACKSLASH,
193 [RGFW_CloseBracket] = KEY_RIGHT_BRACKET,
194 [RGFW_Semicolon] = KEY_SEMICOLON,
195 [RGFW_Insert] = KEY_INSERT,
196 [RGFW_Home] = KEY_HOME,
197 [RGFW_PageUp] = KEY_PAGE_UP,
198 [RGFW_Delete] = KEY_DELETE,
199 [RGFW_End] = KEY_END,
200 [RGFW_PageDown] = KEY_PAGE_DOWN,
201 [RGFW_Right] = KEY_RIGHT,
202 [RGFW_Left] = KEY_LEFT,
203 [RGFW_Down] = KEY_DOWN,
204 [RGFW_Up] = KEY_UP,
205 [RGFW_Numlock] = KEY_NUM_LOCK,
206 [RGFW_KP_Slash] = KEY_KP_DIVIDE,
207 [RGFW_Multiply] = KEY_KP_MULTIPLY,
208 [RGFW_KP_Minus] = KEY_KP_SUBTRACT,
209 [RGFW_KP_Return] = KEY_KP_ENTER,
210 [RGFW_KP_1] = KEY_KP_1,
211 [RGFW_KP_2] = KEY_KP_2,
212 [RGFW_KP_3] = KEY_KP_3,
213 [RGFW_KP_4] = KEY_KP_4,
214 [RGFW_KP_5] = KEY_KP_5,
215 [RGFW_KP_6] = KEY_KP_6,
216 [RGFW_KP_7] = KEY_KP_7,
217 [RGFW_KP_8] = KEY_KP_8,
218 [RGFW_KP_9] = KEY_KP_9,
219 [RGFW_KP_0] = KEY_KP_0,
220 [RGFW_KP_Period] = KEY_KP_DECIMAL
221};
222
223//----------------------------------------------------------------------------------
224// Module Internal Functions Declaration
225//----------------------------------------------------------------------------------
226int InitPlatform(void); // Initialize platform (graphics, inputs and more)
227bool InitGraphicsDevice(void); // Initialize graphics device
228
229//----------------------------------------------------------------------------------
230// Module Functions Declaration
231//----------------------------------------------------------------------------------
232// NOTE: Functions declaration is provided by raylib.h
233
234//----------------------------------------------------------------------------------
235// Module Functions Definition: Window and Graphics Device
236//----------------------------------------------------------------------------------
237
238// Check if application should close
239bool WindowShouldClose(void)
240{
241 if (CORE.Window.shouldClose == false)
242 CORE.Window.shouldClose = RGFW_window_shouldClose(platform.window);
243 if (CORE.Window.ready) return CORE.Window.shouldClose;
244 else return true;
245}
246
247// Toggle fullscreen mode
248void ToggleFullscreen(void)
249{
250 RGFW_window_maximize(platform.window);
251 ToggleBorderlessWindowed();
252}
253
254// Toggle borderless windowed mode
255void ToggleBorderlessWindowed(void)
256{
257 if (platform.window != NULL)
258 {
259 RGFW_window_setBorder(platform.window, CORE.Window.flags & FLAG_WINDOW_UNDECORATED);
260 }
261}
262
263// Set window state: maximized, if resizable
264void MaximizeWindow(void)
265{
266 RGFW_window_maximize(platform.window);
267}
268
269// Set window state: minimized
270void MinimizeWindow(void)
271{
272 RGFW_window_minimize(platform.window);
273}
274
275// Set window state: not minimized/maximized
276void RestoreWindow(void)
277{
278 RGFW_window_restore(platform.window);
279}
280
281// Set window configuration state using flags
282void SetWindowState(unsigned int flags)
283{
284 CORE.Window.flags |= flags;
285
286 if (flags & FLAG_VSYNC_HINT)
287 {
288 RGFW_window_swapInterval(platform.window, 1);
289 }
290 if (flags & FLAG_FULLSCREEN_MODE)
291 {
292 RGFW_window_maximize(platform.window);
293 ToggleBorderlessWindowed();
294 }
295 if (flags & FLAG_WINDOW_RESIZABLE)
296 {
297 RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
298 RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
299 }
300 if (flags & FLAG_WINDOW_UNDECORATED)
301 {
302 ToggleBorderlessWindowed();
303 }
304 if (flags & FLAG_WINDOW_HIDDEN)
305 {
306 RGFW_window_hide(platform.window);
307 }
308 if (flags & FLAG_WINDOW_MINIMIZED)
309 {
310 RGFW_window_minimize(platform.window);
311 }
312 if (flags & FLAG_WINDOW_MAXIMIZED)
313 {
314 RGFW_window_maximize(platform.window);
315 }
316 if (flags & FLAG_WINDOW_UNFOCUSED)
317 {
318 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_RGFW");
319 }
320 if (flags & FLAG_WINDOW_TOPMOST)
321 {
322 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TOPMOST is not supported on PLATFORM_DESKTOP_RGFW");
323 }
324 if (flags & FLAG_WINDOW_ALWAYS_RUN)
325 {
326 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_RGFW");
327 }
328 if (flags & FLAG_WINDOW_TRANSPARENT)
329 {
330 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT post window creation post window creation is not supported on PLATFORM_DESKTOP_RGFW");
331 }
332 if (flags & FLAG_WINDOW_HIGHDPI)
333 {
334 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_RGFW");
335 }
336 if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
337 {
338 RGFW_window_setMousePassthrough(platform.window, flags & FLAG_WINDOW_MOUSE_PASSTHROUGH);
339 }
340 if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
341 {
342 ToggleBorderlessWindowed();
343 }
344 if (flags & FLAG_MSAA_4X_HINT)
345 {
346 RGFW_setGLSamples(4);
347 }
348 if (flags & FLAG_INTERLACED_HINT)
349 {
350 TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_RGFW");
351 }
352}
353
354// Clear window configuration state flags
355void ClearWindowState(unsigned int flags)
356{
357 CORE.Window.flags &= ~flags;
358
359 if (flags & FLAG_VSYNC_HINT)
360 {
361 RGFW_window_swapInterval(platform.window, 0);
362 }
363 if (flags & FLAG_FULLSCREEN_MODE)
364 {
365 ToggleBorderlessWindowed();
366 RGFW_window_restore(platform.window);
367 CORE.Window.fullscreen = false;
368 }
369 if (flags & FLAG_WINDOW_RESIZABLE)
370 {
371 RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0));
372 RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0));
373 }
374 if (flags & FLAG_WINDOW_UNDECORATED)
375 {
376 ToggleBorderlessWindowed();
377 }
378 if (flags & FLAG_WINDOW_HIDDEN)
379 {
380 RGFW_window_show(platform.window);
381 }
382 if (flags & FLAG_WINDOW_MINIMIZED)
383 {
384 RGFW_window_restore(platform.window);
385 }
386 if (flags & FLAG_WINDOW_MAXIMIZED)
387 {
388 RGFW_window_restore(platform.window);
389 }
390 if (flags & FLAG_WINDOW_UNFOCUSED)
391 {
392 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_RGFW");
393 }
394 if (flags & FLAG_WINDOW_TOPMOST)
395 {
396 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TOPMOST is not supported on PLATFORM_DESKTOP_RGFW");
397 }
398 if (flags & FLAG_WINDOW_ALWAYS_RUN)
399 {
400 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_RGFW");
401 }
402 if (flags & FLAG_WINDOW_TRANSPARENT)
403 {
404 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_RGFW");
405 }
406 if (flags & FLAG_WINDOW_HIGHDPI)
407 {
408 // NOTE: There also doesn't seem to be a feature to disable high DPI once enabled
409 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_RGFW");
410 }
411 if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
412 {
413 RGFW_window_setMousePassthrough(platform.window, flags & FLAG_WINDOW_MOUSE_PASSTHROUGH);
414 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_RGFW");
415 }
416 if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
417 {
418 ToggleFullscreen();
419 }
420 if (flags & FLAG_MSAA_4X_HINT)
421 {
422 RGFW_setGLSamples(0);
423 }
424 if (flags & FLAG_INTERLACED_HINT)
425 {
426 TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_RGFW");
427 }
428}
429
430// Set icon for window
431void SetWindowIcon(Image image)
432{
433 i32 channels = 4;
434
435 switch (image.format)
436 {
437 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
438 case PIXELFORMAT_UNCOMPRESSED_R16: // 16 bpp (1 channel - half float)
439 case PIXELFORMAT_UNCOMPRESSED_R32: // 32 bpp (1 channel - float)
440 {
441 channels = 1;
442 } break;
443 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: // 8*2 bpp (2 channels)
444 case PIXELFORMAT_UNCOMPRESSED_R5G6B5: // 16 bpp
445 case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // 24 bpp
446 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: // 16 bpp (1 bit alpha)
447 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: // 16 bpp (4 bit alpha)
448 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: // 32 bpp
449 {
450 channels = 2;
451 } break;
452 case PIXELFORMAT_UNCOMPRESSED_R32G32B32: // 32*3 bpp (3 channels - float)
453 case PIXELFORMAT_UNCOMPRESSED_R16G16B16: // 16*3 bpp (3 channels - half float)
454 case PIXELFORMAT_COMPRESSED_DXT1_RGB: // 4 bpp (no alpha)
455 case PIXELFORMAT_COMPRESSED_ETC1_RGB: // 4 bpp
456 case PIXELFORMAT_COMPRESSED_ETC2_RGB: // 4 bpp
457 case PIXELFORMAT_COMPRESSED_PVRT_RGB: // 4 bpp
458 {
459 channels = 3;
460 } break;
461 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: // 32*4 bpp (4 channels - float)
462 case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: // 16*4 bpp (4 channels - half float)
463 case PIXELFORMAT_COMPRESSED_DXT1_RGBA: // 4 bpp (1 bit alpha)
464 case PIXELFORMAT_COMPRESSED_DXT3_RGBA: // 8 bpp
465 case PIXELFORMAT_COMPRESSED_DXT5_RGBA: // 8 bpp
466 case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: // 8 bpp
467 case PIXELFORMAT_COMPRESSED_PVRT_RGBA: // 4 bpp
468 case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: // 8 bpp
469 case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 2 bpp
470 {
471 channels = 4;
472 } break;
473 default: break;
474 }
475
476 RGFW_window_setIcon(platform.window, image.data, RGFW_AREA(image.width, image.height), channels);
477}
478
479// Set icon for window
480void SetWindowIcons(Image *images, int count)
481{
482 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
483}
484
485// Set title for window
486void SetWindowTitle(const char *title)
487{
488 RGFW_window_setName(platform.window, (char*)title);
489 CORE.Window.title = title;
490}
491
492// Set window position on screen (windowed mode)
493void SetWindowPosition(int x, int y)
494{
495 RGFW_window_move(platform.window, RGFW_POINT(x, y));
496}
497
498// Set monitor for the current window
499void SetWindowMonitor(int monitor)
500{
501 RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors()[monitor]);
502}
503
504// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
505void SetWindowMinSize(int width, int height)
506{
507 RGFW_window_setMinSize(platform.window, RGFW_AREA(width, height));
508 CORE.Window.screenMin.width = width;
509 CORE.Window.screenMin.height = height;
510}
511
512// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
513void SetWindowMaxSize(int width, int height)
514{
515 RGFW_window_setMaxSize(platform.window, RGFW_AREA(width, height));
516 CORE.Window.screenMax.width = width;
517 CORE.Window.screenMax.height = height;
518}
519
520// Set window dimensions
521void SetWindowSize(int width, int height)
522{
523 CORE.Window.screen.width = width;
524 CORE.Window.screen.height = height;
525
526 RGFW_window_resize(platform.window, RGFW_AREA(width, height));
527}
528
529// Set window opacity, value opacity is between 0.0 and 1.0
530void SetWindowOpacity(float opacity)
531{
532 TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
533}
534
535// Set window focused
536void SetWindowFocused(void)
537{
538 RGFW_window_show(platform.window);
539}
540
541// Get native window handle
542void *GetWindowHandle(void)
543{
544#ifdef RGFW_WEBASM
545 return (void*)platform.window->src.ctx;
546#else
547 return (void*)platform.window->src.window;
548#endif
549}
550
551// Get number of monitors
552int GetMonitorCount(void)
553{
554 #define MAX_MONITORS_SUPPORTED 6
555
556 int count = MAX_MONITORS_SUPPORTED;
557 RGFW_monitor *mons = RGFW_getMonitors();
558
559 for (int i = 0; i < 6; i++)
560 {
561 if (!mons[i].rect.x && !mons[i].rect.y && !mons[i].rect.w && mons[i].rect.h)
562 {
563 count = i;
564 break;
565 }
566 }
567
568 return count;
569}
570
571// Get number of monitors
572int GetCurrentMonitor(void)
573{
574 RGFW_monitor *mons = RGFW_getMonitors();
575 RGFW_monitor mon = RGFW_window_getMonitor(platform.window);
576
577 for (int i = 0; i < 6; i++)
578 {
579 if ((mons[i].rect.x == mon.rect.x) && (mons[i].rect.y == mon.rect.y)) return i;
580 }
581
582 return 0;
583}
584
585// Get selected monitor position
586Vector2 GetMonitorPosition(int monitor)
587{
588 RGFW_monitor *mons = RGFW_getMonitors();
589
590 return (Vector2){mons[monitor].rect.x, mons[monitor].rect.y};
591}
592
593// Get selected monitor width (currently used by monitor)
594int GetMonitorWidth(int monitor)
595{
596 RGFW_monitor *mons = RGFW_getMonitors();
597
598 return mons[monitor].rect.w;
599}
600
601// Get selected monitor height (currently used by monitor)
602int GetMonitorHeight(int monitor)
603{
604 RGFW_monitor *mons = RGFW_getMonitors();
605
606 return mons[monitor].rect.h;
607}
608
609// Get selected monitor physical width in millimetres
610int GetMonitorPhysicalWidth(int monitor)
611{
612 RGFW_monitor* mons = RGFW_getMonitors();
613
614 return mons[monitor].physW;
615}
616
617// Get selected monitor physical height in millimetres
618int GetMonitorPhysicalHeight(int monitor)
619{
620 RGFW_monitor *mons = RGFW_getMonitors();
621
622 return mons[monitor].physH;
623}
624
625// Get selected monitor refresh rate
626int GetMonitorRefreshRate(int monitor)
627{
628 TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
629 return 0;
630}
631
632// Get the human-readable, UTF-8 encoded name of the selected monitor
633const char *GetMonitorName(int monitor)
634{
635 RGFW_monitor *mons = RGFW_getMonitors();
636
637 return mons[monitor].name;
638}
639
640// Get window position XY on monitor
641Vector2 GetWindowPosition(void)
642{
643 return (Vector2){ platform.window->r.x, platform.window->r.y };
644}
645
646// Get window scale DPI factor for current monitor
647Vector2 GetWindowScaleDPI(void)
648{
649 RGFW_monitor monitor = RGFW_window_getMonitor(platform.window);
650
651 return (Vector2){monitor.scaleX, monitor.scaleX};
652}
653
654// Set clipboard text content
655void SetClipboardText(const char *text)
656{
657 RGFW_writeClipboard(text, strlen(text));
658}
659
660// Get clipboard text content
661// NOTE: returned string is allocated and freed by GLFW
662const char *GetClipboardText(void)
663{
664 return RGFW_readClipboard(NULL);
665}
666
667
668#if defined(SUPPORT_CLIPBOARD_IMAGE)
669
670#ifdef _WIN32
671# define WIN32_CLIPBOARD_IMPLEMENTATION
672# define WINUSER_ALREADY_INCLUDED
673# define WINBASE_ALREADY_INCLUDED
674# define WINGDI_ALREADY_INCLUDED
675# include "../external/win32_clipboard.h"
676#endif
677
678// Get clipboard image
679Image GetClipboardImage(void)
680{
681 Image image = {0};
682 unsigned long long int dataSize = 0;
683 void* fileData = NULL;
684
685#ifdef _WIN32
686 int width, height;
687 fileData = (void*)Win32GetClipboardImageData(&width, &height, &dataSize);
688#else
689 TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement `GetClipboardImage` for this OS");
690#endif
691
692 if (fileData == NULL)
693 {
694 TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data.");
695 }
696 else
697 {
698 image = LoadImageFromMemory(".bmp", fileData, dataSize);
699 }
700 return image;
701}
702#endif // SUPPORT_CLIPBOARD_IMAGE
703
704// Show mouse cursor
705void ShowCursor(void)
706{
707 RGFW_window_showMouse(platform.window, true);
708 CORE.Input.Mouse.cursorHidden = false;
709}
710
711// Hides mouse cursor
712void HideCursor(void)
713{
714 RGFW_window_showMouse(platform.window, false);
715 CORE.Input.Mouse.cursorHidden = true;
716}
717
718// Enables cursor (unlock cursor)
719void EnableCursor(void)
720{
721 RGFW_disableCursor = false;
722 RGFW_window_mouseUnhold(platform.window);
723
724 // Set cursor position in the middle
725 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
726 RGFW_window_showMouse(platform.window, true);
727 CORE.Input.Mouse.cursorHidden = false;
728}
729
730// Disables cursor (lock cursor)
731void DisableCursor(void)
732{
733 RGFW_disableCursor = true;
734
735 RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0));
736
737 HideCursor();
738}
739
740// Swap back buffer with front buffer (screen drawing)
741void SwapScreenBuffer(void)
742{
743 RGFW_window_swapBuffers(platform.window);
744}
745
746//----------------------------------------------------------------------------------
747// Module Functions Definition: Misc
748//----------------------------------------------------------------------------------
749
750// Get elapsed time measure in seconds since InitTimer()
751double GetTime(void)
752{
753 double time = 0.0;
754 unsigned long long int nanoSeconds = RGFW_getTimeNS();
755 time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
756
757 return time;
758}
759
760// Open URL with default system browser (if available)
761// NOTE: This function is only safe to use if you control the URL given.
762// A user could craft a malicious string performing another action.
763// Only call this function yourself not with user input or make sure to check the string yourself.
764// Ref: https://github.com/raysan5/raylib/issues/686
765void OpenURL(const char *url)
766{
767 // Security check to (partially) avoid malicious code on target platform
768 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
769 else
770 {
771 // TODO: Open URL implementation
772 }
773}
774
775//----------------------------------------------------------------------------------
776// Module Functions Definition: Inputs
777//----------------------------------------------------------------------------------
778
779// Set internal gamepad mappings
780int SetGamepadMappings(const char *mappings)
781{
782 TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
783 return 0;
784}
785
786// Set mouse position XY
787void SetMousePosition(int x, int y)
788{
789 RGFW_window_moveMouse(platform.window, RGFW_POINT(x, y));
790 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
791 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
792}
793
794// Set mouse cursor
795void SetMouseCursor(int cursor)
796{
797 RGFW_window_setMouseStandard(platform.window, cursor);
798}
799
800// Get physical key name.
801const char *GetKeyName(int key)
802{
803 TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
804 return "";
805}
806
807static KeyboardKey ConvertScancodeToKey(u32 keycode);
808
809// TODO: Review function to avoid duplicate with RSGL
810char RSGL_keystrToChar(const char *str)
811{
812 if (str[1] == 0) return str[0];
813
814 static const char *map[] = {
815 "asciitilde", "`",
816 "grave", "~",
817 "exclam", "!",
818 "at", "@",
819 "numbersign", "#",
820 "dollar", "$",
821 "percent", "%%",
822 "asciicircum", "^",
823 "ampersand", "&",
824 "asterisk", "*",
825 "parenleft", "(",
826 "parenright", ")",
827 "underscore", "_",
828 "minus", "-",
829 "plus", "+",
830 "equal", "=",
831 "braceleft", "{",
832 "bracketleft", "[",
833 "bracketright", "]",
834 "braceright", "}",
835 "colon", ":",
836 "semicolon", ";",
837 "quotedbl", "\"",
838 "apostrophe", "'",
839 "bar", "|",
840 "backslash", "\'",
841 "less", "<",
842 "comma", ",",
843 "greater", ">",
844 "period", ".",
845 "question", "?",
846 "slash", "/",
847 "space", " ",
848 "Return", "\n",
849 "Enter", "\n",
850 "enter", "\n",
851 };
852
853 for (unsigned char i = 0; i < (sizeof(map)/sizeof(char *)); i += 2)
854 {
855 if (strcmp(map[i], str) == 0) return *map[i + 1];
856 }
857
858 return '\0';
859}
860
861// Register all input events
862void PollInputEvents(void)
863{
864#if defined(SUPPORT_GESTURES_SYSTEM)
865 // NOTE: Gestures update must be called every frame to reset gestures correctly
866 // because ProcessGestureEvent() is just called on an event, not every frame
867 UpdateGestures();
868#endif
869
870 // Reset keys/chars pressed registered
871 CORE.Input.Keyboard.keyPressedQueueCount = 0;
872 CORE.Input.Keyboard.charPressedQueueCount = 0;
873
874 // Reset mouse wheel
875 CORE.Input.Mouse.currentWheelMove.x = 0;
876 CORE.Input.Mouse.currentWheelMove.y = 0;
877
878 // Register previous mouse position
879
880 // Reset last gamepad button/axis registered state
881
882 for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++)
883 {
884 // Check if gamepad is available
885 if (CORE.Input.Gamepad.ready[i])
886 {
887 // Register previous gamepad button states
888 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
889 {
890 CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
891 }
892 }
893 }
894
895 // Register previous touch states
896 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
897
898 // Map touch position to mouse position for convenience
899 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
900
901 int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
902 bool realTouch = false; // Flag to differentiate real touch gestures from mouse ones
903
904 // Register previous keys states
905 // NOTE: Android supports up to 260 keys
906 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
907 {
908 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
909 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
910 }
911
912 // Register previous mouse states
913 for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
914
915 // Poll input events for current platform
916 //-----------------------------------------------------------------------------
917 CORE.Window.resizedLastFrame = false;
918
919 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
920 #define RGFW_HOLD_MOUSE (1L<<2)
921 if (platform.window->_winArgs & RGFW_HOLD_MOUSE)
922 {
923 CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
924 CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
925 }
926 else
927 {
928 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
929 }
930
931 while (RGFW_window_checkEvent(platform.window))
932 {
933 if ((platform.window->event.type >= RGFW_jsButtonPressed) && (platform.window->event.type <= RGFW_jsAxisMove))
934 {
935 if (!CORE.Input.Gamepad.ready[platform.window->event.joystick])
936 {
937 CORE.Input.Gamepad.ready[platform.window->event.joystick] = true;
938 CORE.Input.Gamepad.axisCount[platform.window->event.joystick] = platform.window->event.axisesCount;
939 CORE.Input.Gamepad.name[platform.window->event.joystick][0] = '\0';
940 CORE.Input.Gamepad.axisState[platform.window->event.joystick][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
941 CORE.Input.Gamepad.axisState[platform.window->event.joystick][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
942 }
943 }
944
945 RGFW_Event *event = &platform.window->event;
946
947 // All input events can be processed after polling
948 switch (event->type)
949 {
950 case RGFW_quit: CORE.Window.shouldClose = true; break;
951 case RGFW_dnd: // Dropped file
952 {
953 for (int i = 0; i < event->droppedFilesCount; i++)
954 {
955 if (CORE.Window.dropFileCount == 0)
956 {
957 // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files
958 // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed
959 // TODO: Pointers should probably be reallocated for any new file added...
960 CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *));
961
962 CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
963 strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]);
964
965 CORE.Window.dropFileCount++;
966 }
967 else if (CORE.Window.dropFileCount < 1024)
968 {
969 CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
970 strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event->droppedFiles[i]);
971
972 CORE.Window.dropFileCount++;
973 }
974 else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!");
975 }
976 } break;
977
978 // Window events are also polled (Minimized, maximized, close...)
979 case RGFW_windowResized:
980 {
981 SetupViewport(platform.window->r.w, platform.window->r.h);
982 CORE.Window.screen.width = platform.window->r.w;
983 CORE.Window.screen.height = platform.window->r.h;
984 CORE.Window.currentFbo.width = platform.window->r.w;
985 CORE.Window.currentFbo.height = platform.window->r.h;
986 CORE.Window.resizedLastFrame = true;
987 } break;
988 case RGFW_windowMoved:
989 {
990 CORE.Window.position.x = platform.window->r.x;
991 CORE.Window.position.y = platform.window->r.x;
992 } break;
993
994 // Keyboard events
995 case RGFW_keyPressed:
996 {
997 KeyboardKey key = ConvertScancodeToKey(event->keyCode);
998
999 if (key != KEY_NULL)
1000 {
1001 // If key was up, add it to the key pressed queue
1002 if ((CORE.Input.Keyboard.currentKeyState[key] == 0) && (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE))
1003 {
1004 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
1005 CORE.Input.Keyboard.keyPressedQueueCount++;
1006 }
1007
1008 CORE.Input.Keyboard.currentKeyState[key] = 1;
1009 }
1010
1011 // TODO: Put exitKey verification outside the switch?
1012 if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey])
1013 {
1014 CORE.Window.shouldClose = true;
1015 }
1016
1017 // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
1018 // Check if there is space available in the queue
1019 if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
1020 {
1021 // Add character (codepoint) to the queue
1022 CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = RSGL_keystrToChar(event->keyName);
1023 CORE.Input.Keyboard.charPressedQueueCount++;
1024 }
1025 } break;
1026 case RGFW_keyReleased:
1027 {
1028 KeyboardKey key = ConvertScancodeToKey(event->keyCode);
1029 if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0;
1030 } break;
1031
1032 // Check mouse events
1033 case RGFW_mouseButtonPressed:
1034 {
1035 if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown))
1036 {
1037 CORE.Input.Mouse.currentWheelMove.y = event->scroll;
1038 break;
1039 }
1040
1041 int btn = event->button;
1042 if (btn == RGFW_mouseLeft) btn = 1;
1043 else if (btn == RGFW_mouseRight) btn = 2;
1044 else if (btn == RGFW_mouseMiddle) btn = 3;
1045
1046 CORE.Input.Mouse.currentButtonState[btn - 1] = 1;
1047 CORE.Input.Touch.currentTouchState[btn - 1] = 1;
1048
1049 touchAction = 1;
1050 } break;
1051 case RGFW_mouseButtonReleased:
1052 {
1053
1054 if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown))
1055 {
1056 CORE.Input.Mouse.currentWheelMove.y = event->scroll;
1057 break;
1058 }
1059
1060 int btn = event->button;
1061 if (btn == RGFW_mouseLeft) btn = 1;
1062 else if (btn == RGFW_mouseRight) btn = 2;
1063 else if (btn == RGFW_mouseMiddle) btn = 3;
1064
1065 CORE.Input.Mouse.currentButtonState[btn - 1] = 0;
1066 CORE.Input.Touch.currentTouchState[btn - 1] = 0;
1067
1068 touchAction = 0;
1069 } break;
1070 case RGFW_mousePosChanged:
1071 {
1072 if (platform.window->_winArgs & RGFW_HOLD_MOUSE)
1073 {
1074 CORE.Input.Mouse.currentPosition.x += (float)event->point.x;
1075 CORE.Input.Mouse.currentPosition.y += (float)event->point.y;
1076 }
1077 else
1078 {
1079 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
1080 CORE.Input.Mouse.currentPosition.x = (float)event->point.x;
1081 CORE.Input.Mouse.currentPosition.y = (float)event->point.y;
1082 }
1083
1084 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
1085 touchAction = 2;
1086 } break;
1087 case RGFW_jsButtonPressed:
1088 {
1089 int button = -1;
1090
1091 switch (event->button)
1092 {
1093 case RGFW_JS_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
1094 case RGFW_JS_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
1095 case RGFW_JS_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
1096 case RGFW_JS_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
1097
1098 case RGFW_JS_L1: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
1099 case RGFW_JS_R1: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
1100
1101 case RGFW_JS_L2: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
1102 case RGFW_JS_R2: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
1103
1104 case RGFW_JS_SELECT: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
1105 case RGFW_JS_HOME: button = GAMEPAD_BUTTON_MIDDLE; break;
1106 case RGFW_JS_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
1107
1108 case RGFW_JS_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
1109 case RGFW_JS_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
1110 case RGFW_JS_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
1111 case RGFW_JS_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
1112
1113 default: break;
1114 }
1115
1116 if (button >= 0)
1117 {
1118 CORE.Input.Gamepad.currentButtonState[event->joystick][button] = 1;
1119 CORE.Input.Gamepad.lastButtonPressed = button;
1120 }
1121 } break;
1122 case RGFW_jsButtonReleased:
1123 {
1124 int button = -1;
1125 switch (event->button)
1126 {
1127 case RGFW_JS_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
1128 case RGFW_JS_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
1129 case RGFW_JS_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
1130 case RGFW_JS_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
1131
1132 case RGFW_JS_L1: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
1133 case RGFW_JS_R1: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
1134
1135 case RGFW_JS_L2: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
1136 case RGFW_JS_R2: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
1137
1138 case RGFW_JS_SELECT: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
1139 case RGFW_JS_HOME: button = GAMEPAD_BUTTON_MIDDLE; break;
1140 case RGFW_JS_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
1141
1142 case RGFW_JS_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
1143 case RGFW_JS_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
1144 case RGFW_JS_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
1145 case RGFW_JS_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
1146 default: break;
1147 }
1148
1149 if (button >= 0)
1150 {
1151 CORE.Input.Gamepad.currentButtonState[event->joystick][button] = 0;
1152 if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
1153 }
1154 } break;
1155 case RGFW_jsAxisMove:
1156 {
1157 int axis = -1;
1158 for (int i = 0; i < event->axisesCount; i++)
1159 {
1160 switch(i)
1161 {
1162 case 0:
1163 {
1164 if (abs(event->axis[i].x) > abs(event->axis[i].y))
1165 {
1166 axis = GAMEPAD_AXIS_LEFT_X;
1167 break;
1168 }
1169
1170 axis = GAMEPAD_AXIS_LEFT_Y;
1171 } break;
1172 case 1:
1173 {
1174 if (abs(event->axis[i].x) > abs(event->axis[i].y))
1175 {
1176 axis = GAMEPAD_AXIS_RIGHT_X;
1177 break;
1178 }
1179
1180 axis = GAMEPAD_AXIS_RIGHT_Y;
1181 } break;
1182 case 2: axis = GAMEPAD_AXIS_LEFT_TRIGGER; break;
1183 case 3: axis = GAMEPAD_AXIS_RIGHT_TRIGGER; break;
1184 default: break;
1185 }
1186
1187 #ifdef __linux__
1188 float value = (event->axis[i].x + event->axis[i].y)/(float)32767;
1189 #else
1190 float value = (event->axis[i].x + -event->axis[i].y)/(float)32767;
1191 #endif
1192 CORE.Input.Gamepad.axisState[event->joystick][axis] = value;
1193
1194 // Register button state for triggers in addition to their axes
1195 if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))
1196 {
1197 int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
1198 int pressed = (value > 0.1f);
1199 CORE.Input.Gamepad.currentButtonState[event->joystick][button] = pressed;
1200
1201 if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
1202 else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
1203 }
1204 }
1205 } break;
1206 default: break;
1207 }
1208
1209#if defined(SUPPORT_GESTURES_SYSTEM)
1210 if (touchAction > -1)
1211 {
1212 // Process mouse events as touches to be able to use mouse-gestures
1213 GestureEvent gestureEvent = { 0 };
1214
1215 // Register touch actions
1216 gestureEvent.touchAction = touchAction;
1217
1218 // Assign a pointer ID
1219 gestureEvent.pointId[0] = 0;
1220
1221 // Register touch points count
1222 gestureEvent.pointCount = 1;
1223
1224 // Register touch points position, only one point registered
1225 if (touchAction == 2 || realTouch) gestureEvent.position[0] = CORE.Input.Touch.position[0];
1226 else gestureEvent.position[0] = GetMousePosition();
1227
1228 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
1229 gestureEvent.position[0].x /= (float)GetScreenWidth();
1230 gestureEvent.position[0].y /= (float)GetScreenHeight();
1231
1232 // Gesture data is sent to gestures-system for processing
1233 ProcessGestureEvent(gestureEvent);
1234
1235 touchAction = -1;
1236 }
1237#endif
1238 }
1239 //-----------------------------------------------------------------------------
1240}
1241
1242//----------------------------------------------------------------------------------
1243// Module Internal Functions Definition
1244//----------------------------------------------------------------------------------
1245
1246// Initialize platform: graphics, inputs and more
1247int InitPlatform(void)
1248{
1249 // Initialize RGFW internal global state, only required systems
1250 unsigned int flags = RGFW_CENTER | RGFW_ALLOW_DND;
1251
1252 // Check window creation flags
1253 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
1254 {
1255 CORE.Window.fullscreen = true;
1256 flags |= RGFW_FULLSCREEN;
1257 }
1258
1259 if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= RGFW_NO_BORDER;
1260 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) flags |= RGFW_NO_RESIZE;
1261
1262 if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= RGFW_TRANSPARENT_WINDOW;
1263
1264 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) flags |= RGFW_FULLSCREEN;
1265
1266 // NOTE: Some OpenGL context attributes must be set before window creation
1267
1268 // Check selection OpenGL version
1269 if (rlGetVersion() == RL_OPENGL_21)
1270 {
1271 RGFW_setGLVersion(RGFW_GL_CORE, 2, 1);
1272 }
1273 else if (rlGetVersion() == RL_OPENGL_33)
1274 {
1275 RGFW_setGLVersion(RGFW_GL_CORE, 3, 3);
1276 }
1277 else if (rlGetVersion() == RL_OPENGL_43)
1278 {
1279 RGFW_setGLVersion(RGFW_GL_CORE, 4, 1);
1280 }
1281
1282 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
1283 {
1284 RGFW_setGLSamples(4);
1285 }
1286
1287 platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags);
1288
1289 RGFW_area screenSize = RGFW_getScreenSize();
1290 CORE.Window.display.width = screenSize.w;
1291 CORE.Window.display.height = screenSize.h;
1292 /*
1293 I think this is needed by Raylib now ?
1294 If so, rcore_destkop_sdl should be updated too
1295 */
1296 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
1297
1298 if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1);
1299
1300 RGFW_window_makeCurrent(platform.window);
1301
1302 // Check surface and context activation
1303 if (platform.window != NULL)
1304 {
1305 CORE.Window.ready = true;
1306
1307 CORE.Window.render.width = CORE.Window.screen.width;
1308 CORE.Window.render.height = CORE.Window.screen.height;
1309 CORE.Window.currentFbo.width = CORE.Window.render.width;
1310 CORE.Window.currentFbo.height = CORE.Window.render.height;
1311
1312 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
1313 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1314 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
1315 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
1316 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
1317 }
1318 else
1319 {
1320 TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
1321 return -1;
1322 }
1323 //----------------------------------------------------------------------------
1324
1325 // If everything work as expected, we can continue
1326 CORE.Window.position.x = platform.window->r.x;
1327 CORE.Window.position.y = platform.window->r.y;
1328 CORE.Window.render.width = CORE.Window.screen.width;
1329 CORE.Window.render.height = CORE.Window.screen.height;
1330 CORE.Window.currentFbo.width = CORE.Window.render.width;
1331 CORE.Window.currentFbo.height = CORE.Window.render.height;
1332
1333 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
1334 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1335 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
1336 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
1337 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
1338
1339 // Load OpenGL extensions
1340 // NOTE: GL procedures address loader is required to load extensions
1341 //----------------------------------------------------------------------------
1342 rlLoadExtensions((void*)RGFW_getProcAddress);
1343 //----------------------------------------------------------------------------
1344
1345 // TODO: Initialize input events system
1346 // It could imply keyboard, mouse, gamepad, touch...
1347 // Depending on the platform libraries/SDK it could use a callback mechanism
1348 // For system events and inputs evens polling on a per-frame basis, use PollInputEvents()
1349 //----------------------------------------------------------------------------
1350 // ...
1351 //----------------------------------------------------------------------------
1352
1353 // Initialize timing system
1354 //----------------------------------------------------------------------------
1355 InitTimer();
1356 //----------------------------------------------------------------------------
1357
1358 // Initialize storage system
1359 //----------------------------------------------------------------------------
1360 CORE.Storage.basePath = GetWorkingDirectory();
1361 //----------------------------------------------------------------------------
1362
1363#ifdef RGFW_X11
1364 for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++)
1365 {
1366 RGFW_registerJoystick(platform.window, i);
1367 }
1368#endif
1369
1370 TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully");
1371
1372 return 0;
1373}
1374
1375// Close platform
1376void ClosePlatform(void)
1377{
1378 RGFW_window_close(platform.window);
1379}
1380
1381// Keycode mapping
1382static KeyboardKey ConvertScancodeToKey(u32 keycode)
1383{
1384 if (keycode > sizeof(keyMappingRGFW)/sizeof(unsigned short)) return 0;
1385
1386 return keyMappingRGFW[keycode];
1387}
diff --git a/raylib/src/platforms/rcore_desktop_sdl.c b/raylib/src/platforms/rcore_desktop_sdl.c
new file mode 100644
index 0000000..a201f2c
--- /dev/null
+++ b/raylib/src/platforms/rcore_desktop_sdl.c
@@ -0,0 +1,1978 @@
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//----------------------------------------------------------------------------------
81typedef 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//----------------------------------------------------------------------------------
93extern CoreData CORE; // Global CORE state context
94
95static PlatformData platform = { 0 }; // Platform specific data
96
97//----------------------------------------------------------------------------------
98// Local Variables Definition
99//----------------------------------------------------------------------------------
100#define SCANCODE_MAPPED_NUM 232
101static 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
225static 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
271const 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
287int 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.
301Uint8 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
313void 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
330SDL_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
340int 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
348SDL_Surface *SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, Uint32 format)
349{
350 return SDL_CreateSurface(width, height, format);
351}
352
353SDL_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
358SDL_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
363int 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.
375int 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
394bool SDL_GetRelativeMouseMode_Adapter(void)
395{
396 return SDL_GetWindowRelativeMouseMode(platform.window);
397}
398
399#define SDL_GetRelativeMouseMode SDL_GetRelativeMouseMode_Adapter
400
401
402int 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)
415void* 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//----------------------------------------------------------------------------------
428int InitPlatform(void); // Initialize platform (graphics, inputs and more)
429void ClosePlatform(void); // Close platform
430
431static 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
443bool WindowShouldClose(void)
444{
445 if (CORE.Window.ready) return CORE.Window.shouldClose;
446 else return true;
447}
448
449// Toggle fullscreen mode
450void 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
478void 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
503void MaximizeWindow(void)
504{
505 SDL_MaximizeWindow(platform.window);
506 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
507}
508
509// Set window state: minimized
510void MinimizeWindow(void)
511{
512 SDL_MinimizeWindow(platform.window);
513 CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;
514}
515
516// Set window state: not minimized/maximized
517void RestoreWindow(void)
518{
519 SDL_ShowWindow(platform.window);
520}
521
522// Set window configuration state using flags
523void 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
620void 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
696void 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
784void SetWindowIcons(Image *images, int count)
785{
786 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
787}
788
789// Set title for window
790void 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)
798void 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
807void 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)
861void 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)
870void 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
879void 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
888void 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
897void SetWindowFocused(void)
898{
899 SDL_RaiseWindow(platform.window);
900}
901
902// Get native window handle
903void *GetWindowHandle(void)
904{
905 return (void *)platform.window;
906}
907
908// Get number of monitors
909int GetMonitorCount(void)
910{
911 int monitorCount = 0;
912
913 monitorCount = SDL_GetNumVideoDisplays();
914
915 return monitorCount;
916}
917
918// Get number of monitors
919int 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
930Vector2 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)
951int 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)
968int 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
985int 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
1005int 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
1025int 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
1042const 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
1053Vector2 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
1064Vector2 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
1083void SetClipboardText(const char *text)
1084{
1085 SDL_SetClipboardText(text);
1086}
1087
1088// Get clipboard text content
1089const 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
1110Image 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
1151void 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
1162void 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)
1173void 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)
1189void 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)
1198void 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
1208double 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
1220void 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
1232int SetGamepadMappings(const char *mappings)
1233{
1234 return SDL_GameControllerAddMapping(mappings);
1235}
1236
1237// Set gamepad vibration
1238void 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
1253void 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
1262void 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.
1271const char *GetKeyName(int key)
1272{
1273 return SDL_GetKeyName(key);
1274}
1275
1276static 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
1310void 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
1774int 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
1960void 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
1969static 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
diff --git a/raylib/src/platforms/rcore_drm.c b/raylib/src/platforms/rcore_drm.c
new file mode 100644
index 0000000..eb8ef01
--- /dev/null
+++ b/raylib/src/platforms/rcore_drm.c
@@ -0,0 +1,1942 @@
1/**********************************************************************************************
2*
3* rcore_drm - Functions to manage window, graphics device and inputs
4*
5* PLATFORM: DRM
6* - Raspberry Pi 0-5 (DRM/KMS)
7* - Linux DRM subsystem (KMS mode)
8*
9* LIMITATIONS:
10* - Most of the window/monitor functions are not implemented (not required)
11*
12* POSSIBLE IMPROVEMENTS:
13* - Improvement 01
14* - Improvement 02
15*
16* ADDITIONAL NOTES:
17* - TRACELOG() function is located in raylib [utils] module
18*
19* CONFIGURATION:
20* #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only)
21* Reconfigure standard input to receive key inputs, works with SSH connection.
22* WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other
23* running processes orblocking the device if not restored properly. Use with care.
24*
25* DEPENDENCIES:
26* - DRM and GLM: System libraries for display initialization and configuration
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#include <fcntl.h> // POSIX file control definitions - open(), creat(), fcntl()
52#include <unistd.h> // POSIX standard function definitions - read(), close(), STDIN_FILENO
53#include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
54#include <pthread.h> // POSIX threads management (inputs reading)
55#include <dirent.h> // POSIX directory browsing
56
57#include <sys/ioctl.h> // Required for: ioctl() - UNIX System call for device-specific input/output operations
58#include <linux/kd.h> // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
59#include <linux/input.h> // Linux: Keycodes constants definition (KEY_A, ...)
60#include <linux/joystick.h> // Linux: Joystick support library
61
62// WARNING: Both 'linux/input.h' and 'raylib.h' define KEY_F12
63// To avoid conflict with the capturing code in rcore.c we undefine the macro KEY_F12,
64// so the enum KEY_F12 from raylib is used
65#undef KEY_F12
66
67#include <gbm.h> // Generic Buffer Management (native platform for EGL on DRM)
68#include <xf86drm.h> // Direct Rendering Manager user-level library interface
69#include <xf86drmMode.h> // Direct Rendering Manager mode setting (KMS) interface
70
71#include "EGL/egl.h" // Native platform windowing system interface
72#include "EGL/eglext.h" // EGL extensions
73
74#ifndef EGL_OPENGL_ES3_BIT
75 #define EGL_OPENGL_ES3_BIT 0x40
76#endif
77
78//----------------------------------------------------------------------------------
79// Defines and Macros
80//----------------------------------------------------------------------------------
81#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number
82
83#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events
84
85// So actually the biggest key is KEY_CNT but we only really map the keys up to
86// KEY_ALS_TOGGLE
87#define KEYMAP_SIZE KEY_ALS_TOGGLE
88
89//----------------------------------------------------------------------------------
90// Types and Structures Definition
91//----------------------------------------------------------------------------------
92
93typedef struct {
94 // Display data
95 int fd; // File descriptor for /dev/dri/...
96 drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector
97 drmModeCrtc *crtc; // CRT Controller
98 int modeIndex; // Index of the used mode of connector->modes
99 struct gbm_device *gbmDevice; // GBM device
100 struct gbm_surface *gbmSurface; // GBM surface
101 struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping)
102 uint32_t prevFB; // Previous GBM framebufer (during frame swapping)
103
104 EGLDisplay device; // Native display device (physical screen connection)
105 EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
106 EGLContext context; // Graphic context, mode in which drawing can be done
107 EGLConfig config; // Graphic config
108
109 // Keyboard data
110 int defaultKeyboardMode; // Default keyboard mode
111 bool eventKeyboardMode; // Keyboard in event mode
112 int defaultFileFlags; // Default IO file flags
113 struct termios defaultSettings; // Default keyboard settings
114 int keyboardFd; // File descriptor for the evdev keyboard
115
116 // Mouse data
117 Vector2 eventWheelMove; // Registers the event mouse wheel variation
118 // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update
119 char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab
120 bool cursorRelative; // Relative cursor mode
121 int mouseFd; // File descriptor for the evdev mouse/touch/gestures
122 Rectangle absRange; // Range of values for absolute pointing devices (touchscreens)
123 int touchSlot; // Hold the touch slot number of the currently being sent multitouch block
124
125 // Gamepad data
126 int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor
127 int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXIS][2]; // [0] = min, [1] = range value of the axis
128 int gamepadAbsAxisMap[MAX_GAMEPADS][ABS_CNT]; // Maps the axes gamepads from the evdev api to a sequential one
129 int gamepadCount; // The number of gamepads registered
130} PlatformData;
131
132//----------------------------------------------------------------------------------
133// Global Variables Definition
134//----------------------------------------------------------------------------------
135extern CoreData CORE; // Global CORE state context
136
137static PlatformData platform = { 0 }; // Platform specific data
138
139//----------------------------------------------------------------------------------
140// Local Variables Definition
141//----------------------------------------------------------------------------------
142
143// NOTE: The complete evdev EV_KEY list can be found at /usr/include/linux/input-event-codes.h
144// TODO: Complete the LUT with all unicode decimal values
145// TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard:
146// Currently non US keyboards will have the wrong mapping for some keys
147// NOTE: Replacing this with the keymap from X11 would probably be useless, as people use the drm
148// backend to *avoid* X11
149static const int evkeyToUnicodeLUT[] = {
150 0, 27, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 8, 0, 113, 119, 101, 114,
151 116, 121, 117, 105, 111, 112, 0, 0, 13, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 59,
152 39, 96, 0, 92, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 32
153 // LUT currently incomplete, just mapped the most essential keys
154};
155
156// This is the map used to map any keycode returned from linux to a raylib code from 'raylib.h'
157// NOTE: Use short here to save a little memory
158static const short linuxToRaylibMap[KEYMAP_SIZE] = {
159 // We don't map those with designated initialization, because we would getting
160 // into loads of naming conflicts
161 0, 256, 49, 50, 51, 52, 53, 54,
162 55, 56, 57, 48, 45, 61, 259, 258,
163 81, 87, 69, 82, 84, 89, 85, 73,
164 79, 80, 91, 93, 257, 341, 65, 83,
165 68, 70, 71, 72, 74, 75, 76, 59,
166 39, 96, 340, 92, 90, 88, 67, 86,
167 66, 78, 77, 44, 46, 47, 344, 332,
168 342, 32, 280, 290, 291, 292, 293, 294,
169 295, 296, 297, 298, 299, 282, 281, 327,
170 328, 329, 333, 324, 325, 326, 334, 321,
171 322, 323, 320, 330, 0, 85, 86, 300,
172 301, 89, 90, 91, 92, 93, 94, 95,
173 335, 345, 331, 283, 346, 101, 268, 265,
174 266, 263, 262, 269, 264, 267, 260, 261,
175 112, 113, 114, 115, 116, 117, 118, 119,
176 120, 121, 122, 123, 124, 125, 347, 127,
177 128, 129, 130, 131, 132, 133, 134, 135,
178 136, 137, 138, 139, 140, 141, 142, 143,
179 144, 145, 146, 147, 148, 149, 150, 151,
180 152, 153, 154, 155, 156, 157, 158, 159,
181 160, 161, 162, 163, 164, 165, 166, 167,
182 168, 169, 170, 171, 172, 173, 174, 175,
183 176, 177, 178, 179, 180, 181, 182, 183,
184 184, 185, 186, 187, 188, 189, 190, 191,
185 192, 193, 194, 0, 0, 0, 0, 0,
186 200, 201, 202, 203, 204, 205, 206, 207,
187 208, 209, 210, 211, 212, 213, 214, 215,
188 216, 217, 218, 219, 220, 221, 222, 223,
189 224, 225, 226, 227, 228, 229, 230, 231,
190 232, 233, 234, 235, 236, 237, 238, 239,
191 240, 241, 242, 243, 244, 245, 246, 247,
192 248, 0, 0, 0, 0, 0, 0, 0,
193
194 // Gamepads are mapped according to:
195 // https://www.kernel.org/doc/html/next/input/gamepad.html
196 // Those mappings are standardized, but that doesn't mean people follow
197 // the standards, so this is more of an approximation
198 [BTN_DPAD_UP] = GAMEPAD_BUTTON_LEFT_FACE_UP,
199 [BTN_DPAD_RIGHT] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
200 [BTN_DPAD_DOWN] = GAMEPAD_BUTTON_LEFT_FACE_DOWN,
201 [BTN_DPAD_LEFT] = GAMEPAD_BUTTON_LEFT_FACE_LEFT,
202 [BTN_Y] = GAMEPAD_BUTTON_RIGHT_FACE_UP,
203 [BTN_B] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
204 [BTN_A] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
205 [BTN_X] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
206 [BTN_TL] = GAMEPAD_BUTTON_LEFT_TRIGGER_1,
207 [BTN_TL2] = GAMEPAD_BUTTON_LEFT_TRIGGER_2,
208 [BTN_TR] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
209 [BTN_TR2] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
210 [BTN_SELECT] = GAMEPAD_BUTTON_MIDDLE_LEFT,
211 [BTN_MODE] = GAMEPAD_BUTTON_MIDDLE,
212 [BTN_START] = GAMEPAD_BUTTON_MIDDLE_RIGHT,
213 [BTN_THUMBL] = GAMEPAD_BUTTON_LEFT_THUMB,
214 [BTN_THUMBR] = GAMEPAD_BUTTON_RIGHT_THUMB,
215};
216
217//----------------------------------------------------------------------------------
218// Module Internal Functions Declaration
219//----------------------------------------------------------------------------------
220int InitPlatform(void); // Initialize platform (graphics, inputs and more)
221void ClosePlatform(void); // Close platform
222
223#if defined(SUPPORT_SSH_KEYBOARD_RPI)
224static void InitKeyboard(void); // Initialize raw keyboard system
225static void RestoreKeyboard(void); // Restore keyboard system
226static void ProcessKeyboard(void); // Process keyboard events
227#endif
228
229static void InitEvdevInput(void); // Initialize evdev inputs
230static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate
231static void PollKeyboardEvents(void); // Process evdev keyboard events
232static void PollGamepadEvents(void); // Process evdev gamepad events
233static void PollMouseEvents(void); // Process evdev mouse events
234
235static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list
236static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list
237static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search the nearest matching DRM connector mode in connector's list
238
239//----------------------------------------------------------------------------------
240// Module Functions Declaration
241//----------------------------------------------------------------------------------
242// NOTE: Functions declaration is provided by raylib.h
243
244//----------------------------------------------------------------------------------
245// Module Functions Definition: Window and Graphics Device
246//----------------------------------------------------------------------------------
247
248// Check if application should close
249// NOTE: By default, if KEY_ESCAPE pressed
250bool WindowShouldClose(void)
251{
252 if (CORE.Window.ready) return CORE.Window.shouldClose;
253 else return true;
254}
255
256// Toggle fullscreen mode
257void ToggleFullscreen(void)
258{
259 TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
260}
261
262// Toggle borderless windowed mode
263void ToggleBorderlessWindowed(void)
264{
265 TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
266}
267
268// Set window state: maximized, if resizable
269void MaximizeWindow(void)
270{
271 TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
272}
273
274// Set window state: minimized
275void MinimizeWindow(void)
276{
277 TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
278}
279
280// Set window state: not minimized/maximized
281void RestoreWindow(void)
282{
283 TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
284}
285
286// Set window configuration state using flags
287void SetWindowState(unsigned int flags)
288{
289 TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
290}
291
292// Clear window configuration state flags
293void ClearWindowState(unsigned int flags)
294{
295 TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
296}
297
298// Set icon for window
299void SetWindowIcon(Image image)
300{
301 TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
302}
303
304// Set icon for window
305void SetWindowIcons(Image *images, int count)
306{
307 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
308}
309
310// Set title for window
311void SetWindowTitle(const char *title)
312{
313 CORE.Window.title = title;
314}
315
316// Set window position on screen (windowed mode)
317void SetWindowPosition(int x, int y)
318{
319 TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
320}
321
322// Set monitor for the current window
323void SetWindowMonitor(int monitor)
324{
325 TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
326}
327
328// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
329void SetWindowMinSize(int width, int height)
330{
331 CORE.Window.screenMin.width = width;
332 CORE.Window.screenMin.height = height;
333}
334
335// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
336void SetWindowMaxSize(int width, int height)
337{
338 CORE.Window.screenMax.width = width;
339 CORE.Window.screenMax.height = height;
340}
341
342// Set window dimensions
343void SetWindowSize(int width, int height)
344{
345 TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
346}
347
348// Set window opacity, value opacity is between 0.0 and 1.0
349void SetWindowOpacity(float opacity)
350{
351 TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
352}
353
354// Set window focused
355void SetWindowFocused(void)
356{
357 TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
358}
359
360// Get native window handle
361void *GetWindowHandle(void)
362{
363 TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
364 return NULL;
365}
366
367// Get number of monitors
368int GetMonitorCount(void)
369{
370 TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
371 return 1;
372}
373
374// Get number of monitors
375int GetCurrentMonitor(void)
376{
377 TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
378 return 0;
379}
380
381// Get selected monitor position
382Vector2 GetMonitorPosition(int monitor)
383{
384 TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
385 return (Vector2){ 0, 0 };
386}
387
388// Get selected monitor width (currently used by monitor)
389int GetMonitorWidth(int monitor)
390{
391 int width = 0;
392
393 if (monitor != 0)
394 {
395 TRACELOG(LOG_WARNING, "GetMonitorWidth() implemented for first monitor only");
396 }
397 else if ((platform.connector) && (platform.modeIndex >= 0))
398 {
399 width = platform.connector->modes[platform.modeIndex].hdisplay;
400 }
401
402 return width;
403}
404
405// Get selected monitor height (currently used by monitor)
406int GetMonitorHeight(int monitor)
407{
408 int height = 0;
409
410 if (monitor != 0)
411 {
412 TRACELOG(LOG_WARNING, "GetMonitorHeight() implemented for first monitor only");
413 }
414 else if ((platform.connector) && (platform.modeIndex >= 0))
415 {
416 height = platform.connector->modes[platform.modeIndex].vdisplay;
417 }
418
419 return height;
420}
421
422// Get selected monitor physical width in millimetres
423int GetMonitorPhysicalWidth(int monitor)
424{
425 int physicalWidth = 0;
426
427 if (monitor != 0)
428 {
429 TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() implemented for first monitor only");
430 }
431 else if ((platform.connector) && (platform.modeIndex >= 0))
432 {
433 physicalWidth = platform.connector->mmWidth;
434 }
435
436 return physicalWidth;
437}
438
439// Get selected monitor physical height in millimetres
440int GetMonitorPhysicalHeight(int monitor)
441{
442 int physicalHeight = 0;
443
444 if (monitor != 0)
445 {
446 TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() implemented for first monitor only");
447 }
448 else if ((platform.connector) && (platform.modeIndex >= 0))
449 {
450 physicalHeight = platform.connector->mmHeight;
451 }
452
453 return physicalHeight;
454}
455
456// Get selected monitor refresh rate
457int GetMonitorRefreshRate(int monitor)
458{
459 int refresh = 0;
460
461 if ((platform.connector) && (platform.modeIndex >= 0))
462 {
463 refresh = platform.connector->modes[platform.modeIndex].vrefresh;
464 }
465
466 return refresh;
467}
468
469// Get the human-readable, UTF-8 encoded name of the selected monitor
470const char *GetMonitorName(int monitor)
471{
472 const char *name = "";
473
474 if (monitor != 0)
475 {
476 TRACELOG(LOG_WARNING, "GetMonitorName() implemented for first monitor only");
477 }
478 else if ((platform.connector) && (platform.modeIndex >= 0))
479 {
480 name = platform.connector->modes[platform.modeIndex].name;
481 }
482
483 return name;
484}
485
486// Get window position XY on monitor
487Vector2 GetWindowPosition(void)
488{
489 return (Vector2){ 0, 0 };
490}
491
492// Get window scale DPI factor for current monitor
493Vector2 GetWindowScaleDPI(void)
494{
495 return (Vector2){ 1.0f, 1.0f };
496}
497
498// Set clipboard text content
499void SetClipboardText(const char *text)
500{
501 TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
502}
503
504// Get clipboard text content
505// NOTE: returned string is allocated and freed by GLFW
506const char *GetClipboardText(void)
507{
508 TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
509 return NULL;
510}
511
512// Show mouse cursor
513void ShowCursor(void)
514{
515 CORE.Input.Mouse.cursorHidden = false;
516}
517
518// Hides mouse cursor
519void HideCursor(void)
520{
521 CORE.Input.Mouse.cursorHidden = true;
522}
523
524// Enables cursor (unlock cursor)
525void EnableCursor(void)
526{
527 // Set cursor position in the middle
528 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
529
530 platform.cursorRelative = false;
531 CORE.Input.Mouse.cursorHidden = false;
532}
533
534// Disables cursor (lock cursor)
535void DisableCursor(void)
536{
537 // Set cursor position in the middle
538 SetMousePosition(0, 0);
539
540 platform.cursorRelative = true;
541 CORE.Input.Mouse.cursorHidden = true;
542}
543
544// Swap back buffer with front buffer (screen drawing)
545void SwapScreenBuffer(void)
546{
547 eglSwapBuffers(platform.device, platform.surface);
548
549 if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
550
551 struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface);
552 if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer");
553
554 uint32_t fb = 0;
555 int result = drmModeAddFB(platform.fd, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb);
556 if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result);
557
558 result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]);
559 if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result);
560
561 if (platform.prevFB)
562 {
563 result = drmModeRmFB(platform.fd, platform.prevFB);
564 if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result);
565 }
566
567 platform.prevFB = fb;
568
569 if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
570
571 platform.prevBO = bo;
572}
573
574//----------------------------------------------------------------------------------
575// Module Functions Definition: Misc
576//----------------------------------------------------------------------------------
577
578// Get elapsed time measure in seconds since InitTimer()
579double GetTime(void)
580{
581 double time = 0.0;
582 struct timespec ts = { 0 };
583 clock_gettime(CLOCK_MONOTONIC, &ts);
584 unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
585
586 time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
587
588 return time;
589}
590
591// Open URL with default system browser (if available)
592// NOTE: This function is only safe to use if you control the URL given.
593// A user could craft a malicious string performing another action.
594// Only call this function yourself not with user input or make sure to check the string yourself.
595// Ref: https://github.com/raysan5/raylib/issues/686
596void OpenURL(const char *url)
597{
598 TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform");
599}
600
601//----------------------------------------------------------------------------------
602// Module Functions Definition: Inputs
603//----------------------------------------------------------------------------------
604
605// Set internal gamepad mappings
606int SetGamepadMappings(const char *mappings)
607{
608 TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
609 return 0;
610}
611
612// Set gamepad vibration
613void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
614{
615 TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform");
616}
617
618// Set mouse position XY
619void SetMousePosition(int x, int y)
620{
621 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
622 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
623}
624
625// Set mouse cursor
626void SetMouseCursor(int cursor)
627{
628 TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
629}
630
631// Get physical key name.
632const char *GetKeyName(int key)
633{
634 TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
635 return "";
636}
637
638// Register all input events
639void PollInputEvents(void)
640{
641#if defined(SUPPORT_GESTURES_SYSTEM)
642 // NOTE: Gestures update must be called every frame to reset gestures correctly
643 // because ProcessGestureEvent() is just called on an event, not every frame
644 UpdateGestures();
645#endif
646
647 // Reset keys/chars pressed registered
648 CORE.Input.Keyboard.keyPressedQueueCount = 0;
649 CORE.Input.Keyboard.charPressedQueueCount = 0;
650
651 // Reset last gamepad button/axis registered state
652 CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
653 //CORE.Input.Gamepad.axisCount = 0;
654
655 // Register previous keys states
656 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
657 {
658 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
659 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
660 }
661
662 PollKeyboardEvents();
663
664#if defined(SUPPORT_SSH_KEYBOARD_RPI)
665 // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
666 // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
667 if (!platform.eventKeyboardMode) ProcessKeyboard();
668#endif
669
670 // Check exit key
671 if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
672
673 // Register previous mouse position
674 if (platform.cursorRelative) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
675 else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
676
677 // Register previous mouse states
678 CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
679 CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove;
680 platform.eventWheelMove = (Vector2){ 0.0f, 0.0f };
681
682 for (int i = 0; i < MAX_MOUSE_BUTTONS; i++)
683 {
684 CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
685 CORE.Input.Mouse.currentButtonState[i] = platform.currentButtonStateEvdev[i];
686 CORE.Input.Touch.currentTouchState[i] = platform.currentButtonStateEvdev[i];
687 }
688
689 // Register gamepads buttons events
690 PollGamepadEvents();
691
692 // Register previous touch states
693 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
694
695 // Reset touch positions
696 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
697
698 // Map touch position to mouse position for convenience
699 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
700
701 // Handle the mouse/touch/gestures events:
702 PollMouseEvents();
703}
704
705//----------------------------------------------------------------------------------
706// Module Internal Functions Definition
707//----------------------------------------------------------------------------------
708
709// Initialize platform: graphics, inputs and more
710int InitPlatform(void)
711{
712 platform.fd = -1;
713 platform.connector = NULL;
714 platform.modeIndex = -1;
715 platform.crtc = NULL;
716 platform.gbmDevice = NULL;
717 platform.gbmSurface = NULL;
718 platform.prevBO = NULL;
719 platform.prevFB = 0;
720
721 // Initialize graphic device: display/window and graphic context
722 //----------------------------------------------------------------------------
723 CORE.Window.fullscreen = true;
724 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
725
726#if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
727 platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
728#else
729 TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card");
730 platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4)
731
732 if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL))
733 {
734 TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1");
735 platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded
736 }
737
738 if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL))
739 {
740 TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0");
741 platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
742 }
743#endif
744
745 if (platform.fd == -1)
746 {
747 TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card");
748 return -1;
749 }
750
751 drmModeRes *res = drmModeGetResources(platform.fd);
752 if (!res)
753 {
754 TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources");
755 return -1;
756 }
757
758 TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors);
759
760 for (size_t i = 0; i < res->count_connectors; i++)
761 {
762 TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i);
763
764 drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]);
765 TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes);
766
767 // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected.
768 // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output.
769 if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id))
770 {
771 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected");
772 platform.connector = con;
773 break;
774 }
775 else
776 {
777 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)");
778 drmModeFreeConnector(con);
779 }
780 }
781
782 if (!platform.connector)
783 {
784 TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found");
785 drmModeFreeResources(res);
786 return -1;
787 }
788
789 drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id);
790 if (!enc)
791 {
792 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder");
793 drmModeFreeResources(res);
794 return -1;
795 }
796
797 platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id);
798 if (!platform.crtc)
799 {
800 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc");
801 drmModeFreeEncoder(enc);
802 drmModeFreeResources(res);
803 return -1;
804 }
805
806 // If InitWindow should use the current mode find it in the connector's mode list
807 if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0))
808 {
809 TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode...");
810
811 platform.modeIndex = FindMatchingConnectorMode(platform.connector, &platform.crtc->mode);
812
813 if (platform.modeIndex < 0)
814 {
815 TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found");
816 drmModeFreeEncoder(enc);
817 drmModeFreeResources(res);
818 return -1;
819 }
820
821 CORE.Window.screen.width = CORE.Window.display.width;
822 CORE.Window.screen.height = CORE.Window.display.height;
823 }
824
825 const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT;
826 const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60;
827
828 // Try to find an exact matching mode
829 platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
830
831 // If nothing found, try to find a nearly matching mode
832 if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
833
834 // If nothing found, try to find an exactly matching mode including interlaced
835 if (platform.modeIndex < 0) platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
836
837 // If nothing found, try to find a nearly matching mode including interlaced
838 if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
839
840 // If nothing found, there is no suitable mode
841 if (platform.modeIndex < 0)
842 {
843 TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode");
844 drmModeFreeEncoder(enc);
845 drmModeFreeResources(res);
846 return -1;
847 }
848
849 CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay;
850 CORE.Window.display.height = platform.connector->modes[platform.modeIndex].vdisplay;
851
852 TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name,
853 platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay,
854 (platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
855 platform.connector->modes[platform.modeIndex].vrefresh);
856
857 // Use the width and height of the surface for render
858 CORE.Window.render.width = CORE.Window.screen.width;
859 CORE.Window.render.height = CORE.Window.screen.height;
860
861 drmModeFreeEncoder(enc);
862 enc = NULL;
863
864 drmModeFreeResources(res);
865 res = NULL;
866
867 platform.gbmDevice = gbm_create_device(platform.fd);
868 if (!platform.gbmDevice)
869 {
870 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device");
871 return -1;
872 }
873
874 platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay,
875 platform.connector->modes[platform.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
876 if (!platform.gbmSurface)
877 {
878 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface");
879 return -1;
880 }
881
882 EGLint samples = 0;
883 EGLint sampleBuffer = 0;
884 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
885 {
886 samples = 4;
887 sampleBuffer = 1;
888 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
889 }
890
891 const EGLint framebufferAttribs[] =
892 {
893 EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support
894 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android!
895 EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5)
896 EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6)
897 EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5)
898 EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer)
899 //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
900 EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
901 //EGL_STENCIL_SIZE, 8, // Stencil buffer size
902 EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
903 EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
904 EGL_NONE
905 };
906
907 const EGLint contextAttribs[] = {
908 EGL_CONTEXT_CLIENT_VERSION, 2,
909 EGL_NONE
910 };
911
912 EGLint numConfigs = 0;
913
914 // Get an EGL device connection
915 platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice);
916 if (platform.device == EGL_NO_DISPLAY)
917 {
918 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
919 return -1;
920 }
921
922 // Initialize the EGL device connection
923 if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
924 {
925 // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
926 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
927 return -1;
928 }
929
930 if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs))
931 {
932 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
933 return -1;
934 }
935
936 TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs);
937
938 EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs));
939 if (!configs)
940 {
941 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
942 return -1;
943 }
944
945 EGLint matchingNumConfigs = 0;
946 if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
947 {
948 TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
949 free(configs);
950 return -1;
951 }
952
953 TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs);
954
955 // find the EGL config that matches the previously setup GBM format
956 int found = 0;
957 for (EGLint i = 0; i < matchingNumConfigs; ++i)
958 {
959 EGLint id = 0;
960 if (!eglGetConfigAttrib(platform.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
961 {
962 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError());
963 continue;
964 }
965
966 if (GBM_FORMAT_ARGB8888 == id)
967 {
968 TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i);
969 platform.config = configs[i];
970 found = 1;
971 break;
972 }
973 }
974
975 RL_FREE(configs);
976
977 if (!found)
978 {
979 TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config");
980 return -1;
981 }
982
983 // Set rendering API
984 eglBindAPI(EGL_OPENGL_ES_API);
985
986 // Create an EGL rendering context
987 platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
988 if (platform.context == EGL_NO_CONTEXT)
989 {
990 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
991 return -1;
992 }
993
994 // Create an EGL window surface
995 platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL);
996 if (EGL_NO_SURFACE == platform.surface)
997 {
998 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
999 return -1;
1000 }
1001
1002 // At this point we need to manage render size vs screen size
1003 // NOTE: This function use and modify global module variables:
1004 // -> CORE.Window.screen.width/CORE.Window.screen.height
1005 // -> CORE.Window.render.width/CORE.Window.render.height
1006 // -> CORE.Window.screenScale
1007 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
1008
1009 // There must be at least one frame displayed before the buffers are swapped
1010 //eglSwapInterval(platform.device, 1);
1011
1012 EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
1013
1014 // Check surface and context activation
1015 if (result != EGL_FALSE)
1016 {
1017 CORE.Window.ready = true;
1018
1019 CORE.Window.render.width = CORE.Window.screen.width;
1020 CORE.Window.render.height = CORE.Window.screen.height;
1021 CORE.Window.currentFbo.width = CORE.Window.render.width;
1022 CORE.Window.currentFbo.height = CORE.Window.render.height;
1023
1024 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
1025 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1026 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
1027 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
1028 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
1029 }
1030 else
1031 {
1032 TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
1033 return -1;
1034 }
1035
1036 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
1037
1038 // If graphic device is no properly initialized, we end program
1039 if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
1040 else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
1041
1042 // Set some default window flags
1043 CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false
1044 CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false
1045 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true
1046 CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false
1047
1048 // Load OpenGL extensions
1049 // NOTE: GL procedures address loader is required to load extensions
1050 rlLoadExtensions(eglGetProcAddress);
1051 //----------------------------------------------------------------------------
1052
1053 // Initialize timming system
1054 //----------------------------------------------------------------------------
1055 // NOTE: timming system must be initialized before the input events system
1056 InitTimer();
1057 //----------------------------------------------------------------------------
1058
1059 // Initialize input events system
1060 //----------------------------------------------------------------------------
1061 InitEvdevInput(); // Evdev inputs initialization
1062
1063#if defined(SUPPORT_SSH_KEYBOARD_RPI)
1064 InitKeyboard(); // Keyboard init (stdin)
1065#endif
1066 //----------------------------------------------------------------------------
1067
1068 // Initialize storage system
1069 //----------------------------------------------------------------------------
1070 CORE.Storage.basePath = GetWorkingDirectory();
1071 //----------------------------------------------------------------------------
1072
1073 TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully");
1074
1075 return 0;
1076}
1077
1078// Close platform
1079void ClosePlatform(void)
1080{
1081 if (platform.prevFB)
1082 {
1083 drmModeRmFB(platform.fd, platform.prevFB);
1084 platform.prevFB = 0;
1085 }
1086
1087 if (platform.prevBO)
1088 {
1089 gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
1090 platform.prevBO = NULL;
1091 }
1092
1093 if (platform.gbmSurface)
1094 {
1095 gbm_surface_destroy(platform.gbmSurface);
1096 platform.gbmSurface = NULL;
1097 }
1098
1099 if (platform.gbmDevice)
1100 {
1101 gbm_device_destroy(platform.gbmDevice);
1102 platform.gbmDevice = NULL;
1103 }
1104
1105 if (platform.crtc)
1106 {
1107 if (platform.connector)
1108 {
1109 drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id,
1110 platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode);
1111 drmModeFreeConnector(platform.connector);
1112 platform.connector = NULL;
1113 }
1114
1115 drmModeFreeCrtc(platform.crtc);
1116 platform.crtc = NULL;
1117 }
1118
1119 if (platform.fd != -1)
1120 {
1121 close(platform.fd);
1122 platform.fd = -1;
1123 }
1124
1125 // Close surface, context and display
1126 if (platform.device != EGL_NO_DISPLAY)
1127 {
1128 if (platform.surface != EGL_NO_SURFACE)
1129 {
1130 eglDestroySurface(platform.device, platform.surface);
1131 platform.surface = EGL_NO_SURFACE;
1132 }
1133
1134 if (platform.context != EGL_NO_CONTEXT)
1135 {
1136 eglDestroyContext(platform.device, platform.context);
1137 platform.context = EGL_NO_CONTEXT;
1138 }
1139
1140 eglTerminate(platform.device);
1141 platform.device = EGL_NO_DISPLAY;
1142 }
1143
1144 CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called
1145
1146 // Close the evdev devices
1147
1148 if (platform.mouseFd != -1)
1149 {
1150 close(platform.mouseFd);
1151 platform.mouseFd = -1;
1152 }
1153
1154 for (int i = 0; i < platform.gamepadCount; i++)
1155 {
1156 close(platform.gamepadStreamFd[i]);
1157 platform.gamepadStreamFd[i] = -1;
1158 }
1159
1160 if (platform.keyboardFd != -1)
1161 {
1162 close(platform.keyboardFd);
1163 platform.keyboardFd = -1;
1164 }
1165}
1166
1167#if defined(SUPPORT_SSH_KEYBOARD_RPI)
1168// Initialize Keyboard system (using standard input)
1169static void InitKeyboard(void)
1170{
1171 // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor,
1172 // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
1173
1174 // Save terminal keyboard settings
1175 tcgetattr(STDIN_FILENO, &platform.defaultSettings);
1176
1177 // Reconfigure terminal with new settings
1178 struct termios keyboardNewSettings = { 0 };
1179 keyboardNewSettings = platform.defaultSettings;
1180
1181 // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
1182 // NOTE: ISIG controls if ^C and ^Z generate break signals or not
1183 keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG);
1184 //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
1185 keyboardNewSettings.c_cc[VMIN] = 1;
1186 keyboardNewSettings.c_cc[VTIME] = 0;
1187
1188 // Set new keyboard settings (change occurs immediately)
1189 tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings);
1190
1191 // Save old keyboard mode to restore it at the end
1192 platform.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags
1193 fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified
1194
1195 // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno)
1196 int result = ioctl(STDIN_FILENO, KDGKBMODE, &platform.defaultKeyboardMode);
1197
1198 // In case of failure, it could mean a remote keyboard is used (SSH)
1199 if (result < 0) TRACELOG(LOG_WARNING, "DRM: Failed to change keyboard mode, an SSH keyboard is probably used");
1200 else
1201 {
1202 // Reconfigure keyboard mode to get:
1203 // - scancodes (K_RAW)
1204 // - keycodes (K_MEDIUMRAW)
1205 // - ASCII chars (K_XLATE)
1206 // - UNICODE chars (K_UNICODE)
1207 ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); // ASCII chars
1208 }
1209
1210 // Register keyboard restore when program finishes
1211 atexit(RestoreKeyboard);
1212}
1213
1214// Restore default keyboard input
1215static void RestoreKeyboard(void)
1216{
1217 // Reset to default keyboard settings
1218 tcsetattr(STDIN_FILENO, TCSANOW, &platform.defaultSettings);
1219
1220 // Reconfigure keyboard to default mode
1221 fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags);
1222 ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode);
1223}
1224
1225// Process keyboard inputs
1226static void ProcessKeyboard(void)
1227{
1228 #define MAX_KEYBUFFER_SIZE 32 // Max size in bytes to read
1229
1230 // Keyboard input polling (fill keys[256] array with status)
1231 int bufferByteCount = 0; // Bytes available on the buffer
1232 char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 }; // Max keys to be read at a time
1233
1234 // Read availables keycodes from stdin
1235 bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE); // POSIX system call
1236
1237 // Reset pressed keys array (it will be filled below)
1238 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
1239 {
1240 CORE.Input.Keyboard.currentKeyState[i] = 0;
1241 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
1242 }
1243
1244 // Fill all read bytes (looking for keys)
1245 for (int i = 0; i < bufferByteCount; i++)
1246 {
1247 // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code!
1248 // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42
1249 if (keysBuffer[i] == 0x1b)
1250 {
1251 // Check if ESCAPE key has been pressed to stop program
1252 if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1;
1253 else
1254 {
1255 if (keysBuffer[i + 1] == 0x5b) // Special function key
1256 {
1257 if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32))
1258 {
1259 // Process special function keys (F1 - F12)
1260 switch (keysBuffer[i + 3])
1261 {
1262 case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break; // raylib KEY_F1
1263 case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break; // raylib KEY_F2
1264 case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break; // raylib KEY_F3
1265 case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break; // raylib KEY_F4
1266 case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break; // raylib KEY_F5
1267 case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break; // raylib KEY_F6
1268 case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break; // raylib KEY_F7
1269 case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break; // raylib KEY_F8
1270 case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break; // raylib KEY_F9
1271 case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break; // raylib KEY_F10
1272 case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break; // raylib KEY_F11
1273 case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break; // raylib KEY_F12
1274 default: break;
1275 }
1276
1277 if (keysBuffer[i + 2] == 0x5b) i += 4;
1278 else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5;
1279 }
1280 else
1281 {
1282 switch (keysBuffer[i + 2])
1283 {
1284 case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break; // raylib KEY_UP
1285 case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break; // raylib KEY_DOWN
1286 case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break; // raylib KEY_RIGHT
1287 case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break; // raylib KEY_LEFT
1288 default: break;
1289 }
1290
1291 i += 3; // Jump to next key
1292 }
1293
1294 // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT)
1295 }
1296 }
1297 }
1298 else if (keysBuffer[i] == 0x0a) // raylib KEY_ENTER (don't mix with <linux/input.h> KEY_*)
1299 {
1300 CORE.Input.Keyboard.currentKeyState[257] = 1;
1301
1302 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue
1303 CORE.Input.Keyboard.keyPressedQueueCount++;
1304 }
1305 else if (keysBuffer[i] == 0x7f) // raylib KEY_BACKSPACE
1306 {
1307 CORE.Input.Keyboard.currentKeyState[259] = 1;
1308
1309 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue
1310 CORE.Input.Keyboard.keyPressedQueueCount++;
1311 }
1312 else
1313 {
1314 // Translate lowercase a-z letters to A-Z
1315 if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122))
1316 {
1317 CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1;
1318 }
1319 else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1;
1320
1321 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i]; // Add keys pressed into queue
1322 CORE.Input.Keyboard.keyPressedQueueCount++;
1323 }
1324 }
1325}
1326#endif // SUPPORT_SSH_KEYBOARD_RPI
1327
1328// Initialise user input from evdev(/dev/input/event<N>)
1329// this means mouse, keyboard or gamepad devices
1330static void InitEvdevInput(void)
1331{
1332 char path[MAX_FILEPATH_LENGTH] = { 0 };
1333 DIR *directory = NULL;
1334 struct dirent *entity = NULL;
1335
1336 // Initialise keyboard file descriptor
1337 platform.keyboardFd = -1;
1338 platform.mouseFd = -1;
1339
1340 // Reset variables
1341 for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
1342 {
1343 CORE.Input.Touch.position[i].x = -1;
1344 CORE.Input.Touch.position[i].y = -1;
1345 }
1346
1347 // Reset keyboard key state
1348 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
1349 {
1350 CORE.Input.Keyboard.currentKeyState[i] = 0;
1351 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
1352 }
1353
1354 // Open the linux directory of "/dev/input"
1355 directory = opendir(DEFAULT_EVDEV_PATH);
1356
1357 if (directory)
1358 {
1359 while ((entity = readdir(directory)) != NULL)
1360 {
1361 if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*"
1362 (strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*"
1363 {
1364 sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
1365 ConfigureEvdevDevice(path); // Configure the device if appropriate
1366 }
1367 }
1368
1369 closedir(directory);
1370 }
1371 else TRACELOG(LOG_WARNING, "INPUT: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH);
1372}
1373
1374// Identifies a input device and configures it for use if appropriate
1375static void ConfigureEvdevDevice(char *device)
1376{
1377 #define BITS_PER_LONG (8*sizeof(long))
1378 #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1)
1379 #define OFF(x) ((x)%BITS_PER_LONG)
1380 #define BIT(x) (1UL<<OFF(x))
1381 #define LONG(x) ((x)/BITS_PER_LONG)
1382 #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
1383
1384 unsigned long evBits[NBITS(EV_MAX)] = { 0 };
1385 unsigned long absBits[NBITS(ABS_MAX)] = { 0 };
1386 unsigned long relBits[NBITS(REL_MAX)] = { 0 };
1387 unsigned long keyBits[NBITS(KEY_MAX)] = { 0 };
1388
1389 // Open the device
1390 int fd = open(device, O_RDONLY | O_NONBLOCK);
1391 if (fd < 0)
1392 {
1393 TRACELOG(LOG_WARNING, "DRM: Failed to open input device: %s", device);
1394 return;
1395 }
1396
1397 // At this point we have a connection to the device, but we don't yet know what the device is.
1398 // It could be many things, even as simple as a power button...
1399 //-------------------------------------------------------------------------------------------------------
1400
1401 // Identify the device
1402 //-------------------------------------------------------------------------------------------------------
1403 struct {
1404 bool exist;
1405 struct input_absinfo info;
1406 } absinfo[ABS_CNT] = { 0 };
1407
1408 // These flags aren't really a one of
1409 // Some devices could have properties we assosciate with keyboards as well as properties
1410 // we assosciate with mice
1411 bool isKeyboard = false;
1412 bool isMouse = false;
1413 bool isTouch = false;
1414 bool isGamepad = false;
1415
1416 int absAxisCount = 0;
1417
1418 ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits);
1419 ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
1420
1421 if (TEST_BIT(evBits, EV_ABS))
1422 {
1423 ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
1424
1425 // If the device has an X an Y axis it's either a touch device, a special mouse or a gamepad
1426 bool hasAbsXY = TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y);
1427
1428 if (hasAbsXY)
1429 {
1430 absAxisCount += 2;
1431
1432 absinfo[ABS_X].exist = true;
1433 absinfo[ABS_Y].exist = true;
1434
1435 ioctl(fd, EVIOCGABS(ABS_X), &absinfo[ABS_X].info);
1436 ioctl(fd, EVIOCGABS(ABS_Y), &absinfo[ABS_Y].info);
1437 }
1438
1439 // If it has any of these buttons it's a touch device
1440 if (hasAbsXY &&
1441 (TEST_BIT(keyBits, BTN_STYLUS) ||
1442 TEST_BIT(keyBits, BTN_TOOL_PEN) ||
1443 TEST_BIT(keyBits, BTN_TOOL_FINGER) ||
1444 TEST_BIT(keyBits, BTN_TOUCH))) isTouch = true;
1445
1446 // Absolute mice should really only exist with VMWare, but it shouldn't
1447 // matter if we support them
1448 else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
1449
1450 // If any of the common joystick axis is present, we assume it's a gamepad
1451 else
1452 {
1453 for (int axis = (hasAbsXY? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++)
1454 {
1455 if (TEST_BIT(absBits, axis))
1456 {
1457 absinfo[axis].exist = true;
1458 isGamepad = true;
1459 absAxisCount++;
1460
1461 ioctl(fd, EVIOCGABS(axis), &absinfo[axis].info);
1462 }
1463 }
1464 }
1465
1466 // If the device has multitouch axes, it's a touch device
1467 if (TEST_BIT(absBits, ABS_MT_POSITION_X) &&
1468 TEST_BIT(absBits, ABS_MT_POSITION_Y)) isTouch = true;
1469 }
1470
1471 if (TEST_BIT(evBits, EV_REL))
1472 {
1473 ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
1474
1475 // If it has any of the gamepad or touch features we tested so far, it's not a mouse
1476 if (!isTouch &&
1477 !isGamepad &&
1478 TEST_BIT(relBits, REL_X) &&
1479 TEST_BIT(relBits, REL_Y) &&
1480 TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
1481 }
1482
1483 if (TEST_BIT(evBits, EV_KEY))
1484 {
1485 // The first 32 keys as defined in input-event-codes.h are pretty much
1486 // exclusive to keyboards, so we can test them using a mask
1487 // Leave out the first bit to not test KEY_RESERVED
1488 const unsigned long mask = 0xFFFFFFFE;
1489 if ((keyBits[0] & mask) == mask) isKeyboard = true;
1490
1491 // If we find any of the common gamepad buttons we assume it's a gamepad
1492 else
1493 {
1494 for (int button = BTN_JOYSTICK; button < BTN_DIGI; ++button)
1495 {
1496 if (TEST_BIT(keyBits, button)) isGamepad = true;
1497 }
1498
1499 for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40; button++)
1500 {
1501 if (TEST_BIT(keyBits, button)) isGamepad = true;
1502 }
1503 }
1504 }
1505
1506 const char *deviceKindStr = "unknown";
1507 if (isMouse || isTouch)
1508 {
1509 deviceKindStr = "mouse";
1510 if (platform.mouseFd != -1) close(platform.mouseFd);
1511 platform.mouseFd = fd;
1512
1513 if (absAxisCount > 0)
1514 {
1515 platform.absRange.x = absinfo[ABS_X].info.minimum;
1516 platform.absRange.width = absinfo[ABS_X].info.maximum - absinfo[ABS_X].info.minimum;
1517
1518 platform.absRange.y = absinfo[ABS_Y].info.minimum;
1519 platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum;
1520 }
1521 }
1522 else if (isGamepad && !isMouse && !isKeyboard && platform.gamepadCount < MAX_GAMEPADS)
1523 {
1524 deviceKindStr = "gamepad";
1525 int index = platform.gamepadCount++;
1526
1527 platform.gamepadStreamFd[index] = fd;
1528 CORE.Input.Gamepad.ready[index] = true;
1529
1530 ioctl(platform.gamepadStreamFd[index], EVIOCGNAME(64), &CORE.Input.Gamepad.name[index]);
1531 CORE.Input.Gamepad.axisCount[index] = absAxisCount;
1532
1533 if (absAxisCount > 0)
1534 {
1535 // TODO / NOTE
1536 // So gamepad axis (as in the actual linux joydev.c) are just simply enumerated
1537 // and (at least for some input drivers like xpat) it's convention to use
1538 // ABS_X, ABX_Y for one joystick ABS_RX, ABS_RY for the other and the Z axes for the
1539 // shoulder buttons
1540 // If these are now enumerated you get LJOY_X, LJOY_Y, LEFT_SHOULDERB, RJOY_X, ...
1541 // That means they don't match the GamepadAxis enum
1542 // This could be fixed
1543 int axisIndex = 0;
1544 for (int axis = ABS_X; axis < ABS_PRESSURE; axis++)
1545 {
1546 if (absinfo[axis].exist)
1547 {
1548 platform.gamepadAbsAxisRange[index][axisIndex][0] = absinfo[axisIndex].info.minimum;
1549 platform.gamepadAbsAxisRange[index][axisIndex][1] = absinfo[axisIndex].info.maximum - absinfo[axisIndex].info.minimum;
1550
1551 platform.gamepadAbsAxisMap[index][axis] = axisIndex;
1552 axisIndex++;
1553 }
1554 }
1555 }
1556 }
1557 else if (isKeyboard && (platform.keyboardFd == -1))
1558 {
1559 deviceKindStr = "keyboard";
1560 platform.keyboardFd = fd;
1561 }
1562 else
1563 {
1564 close(fd);
1565 return;
1566 }
1567
1568 TRACELOG(LOG_INFO, "INPUT: Initialized input device %s as %s", device, deviceKindStr);
1569}
1570
1571// Poll and process evdev keyboard events
1572static void PollKeyboardEvents(void)
1573{
1574 int fd = platform.keyboardFd;
1575 if (fd == -1) return;
1576
1577 struct input_event event = { 0 };
1578 int keycode = -1;
1579
1580 // Try to read data from the keyboard and only continue if successful
1581 while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
1582 {
1583 // Check if the event is a key event
1584 if (event.type != EV_KEY) continue;
1585
1586#if defined(SUPPORT_SSH_KEYBOARD_RPI)
1587 // If the event was a key, we know a working keyboard is connected, so disable the SSH keyboard
1588 platform.eventKeyboardMode = true;
1589#endif
1590
1591 // Keyboard keys appear for codes 1 to 255, ignore everthing else
1592 if ((event.code >= 1) && (event.code <= 255))
1593 {
1594
1595 // Lookup the scancode in the keymap to get a keycode
1596 keycode = linuxToRaylibMap[event.code];
1597
1598 // Make sure we got a valid keycode
1599 if ((keycode > 0) && (keycode < MAX_KEYBOARD_KEYS))
1600 {
1601
1602 // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
1603 // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL,
1604 // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat
1605 CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1);
1606 CORE.Input.Keyboard.keyRepeatInFrame[keycode] = (event.value == 2);
1607
1608 // If the key is pressed add it to the queues
1609 if (event.value == 1)
1610 {
1611 if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
1612 {
1613 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;
1614 CORE.Input.Keyboard.keyPressedQueueCount++;
1615 }
1616
1617 if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
1618 {
1619 // TODO/FIXME: This is not actually converting to unicode properly because it's not taking things like shift into account
1620 CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = evkeyToUnicodeLUT[event.code];
1621 CORE.Input.Keyboard.charPressedQueueCount++;
1622 }
1623 }
1624
1625 TRACELOG(LOG_DEBUG, "INPUT: KEY_%s Keycode(linux): %4i KeyCode(raylib): %4i", (event.value == 0)? "UP " : "DOWN", event.code, keycode);
1626 }
1627 }
1628 }
1629}
1630
1631// Poll gamepad input events
1632static void PollGamepadEvents(void)
1633{
1634 // Read gamepad event
1635 struct input_event event = { 0 };
1636
1637 for (int i = 0; i < platform.gamepadCount; i++)
1638 {
1639 if (!CORE.Input.Gamepad.ready[i]) continue;
1640
1641 // Register previous gamepad states
1642 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
1643
1644 while (read(platform.gamepadStreamFd[i], &event, sizeof(event)) == (int)sizeof(event))
1645 {
1646 if (event.type == EV_KEY)
1647 {
1648 if (event.code < KEYMAP_SIZE)
1649 {
1650 short keycodeRaylib = linuxToRaylibMap[event.code];
1651
1652 TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: KEY_%s Keycode(linux): %4i Keycode(raylib): %4i", i, (event.value == 0)? "UP" : "DOWN", event.code, keycodeRaylib);
1653
1654 if ((keycodeRaylib != 0) && (keycodeRaylib < MAX_GAMEPAD_BUTTONS))
1655 {
1656 // 1 - button pressed, 0 - button released
1657 CORE.Input.Gamepad.currentButtonState[i][keycodeRaylib] = event.value;
1658
1659 CORE.Input.Gamepad.lastButtonPressed = (event.value == 1)? keycodeRaylib : GAMEPAD_BUTTON_UNKNOWN;
1660 }
1661 }
1662 }
1663 else if (event.type == EV_ABS)
1664 {
1665 if (event.code < ABS_CNT)
1666 {
1667 int axisRaylib = platform.gamepadAbsAxisMap[i][event.code];
1668
1669 TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: Axis: %2i Value: %i", i, axisRaylib, event.value);
1670
1671 if (axisRaylib < MAX_GAMEPAD_AXIS)
1672 {
1673 int min = platform.gamepadAbsAxisRange[i][event.code][0];
1674 int range = platform.gamepadAbsAxisRange[i][event.code][1];
1675
1676 // NOTE: Scaling of event.value to get values between -1..1
1677 CORE.Input.Gamepad.axisState[i][axisRaylib] = (2*(float)(event.value - min)/range) - 1;
1678 }
1679 }
1680 }
1681 }
1682 }
1683}
1684
1685// Poll mouse input events
1686static void PollMouseEvents(void)
1687{
1688 int fd = platform.mouseFd;
1689 if (fd == -1) return;
1690
1691 struct input_event event = { 0 };
1692 int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
1693
1694 // Try to read data from the mouse/touch/gesture and only continue if successful
1695 while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
1696 {
1697 // Relative movement parsing
1698 if (event.type == EV_REL)
1699 {
1700 if (event.code == REL_X)
1701 {
1702 if (platform.cursorRelative)
1703 {
1704 CORE.Input.Mouse.currentPosition.x = event.value;
1705 CORE.Input.Mouse.previousPosition.x = 0.0f;
1706 }
1707 else CORE.Input.Mouse.currentPosition.x += event.value;
1708
1709 CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
1710 touchAction = 2; // TOUCH_ACTION_MOVE
1711 }
1712
1713 if (event.code == REL_Y)
1714 {
1715 if (platform.cursorRelative)
1716 {
1717 CORE.Input.Mouse.currentPosition.y = event.value;
1718 CORE.Input.Mouse.previousPosition.y = 0.0f;
1719 }
1720 else CORE.Input.Mouse.currentPosition.y += event.value;
1721
1722 CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
1723 touchAction = 2; // TOUCH_ACTION_MOVE
1724 }
1725
1726 if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value;
1727 }
1728
1729 // Absolute movement parsing
1730 if (event.type == EV_ABS)
1731 {
1732 // Basic movement
1733 if (event.code == ABS_X)
1734 {
1735 CORE.Input.Mouse.currentPosition.x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange
1736 CORE.Input.Touch.position[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange
1737
1738 touchAction = 2; // TOUCH_ACTION_MOVE
1739 }
1740
1741 if (event.code == ABS_Y)
1742 {
1743 CORE.Input.Mouse.currentPosition.y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange
1744 CORE.Input.Touch.position[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange
1745
1746 touchAction = 2; // TOUCH_ACTION_MOVE
1747 }
1748
1749 // Multitouch movement
1750 if (event.code == ABS_MT_SLOT) platform.touchSlot = event.value; // Remember the slot number for the folowing events
1751
1752 if (event.code == ABS_MT_POSITION_X)
1753 {
1754 if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width; // Scale according to absRange
1755 }
1756
1757 if (event.code == ABS_MT_POSITION_Y)
1758 {
1759 if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height; // Scale according to absRange
1760 }
1761
1762 if (event.code == ABS_MT_TRACKING_ID)
1763 {
1764 if ((event.value < 0) && (platform.touchSlot < MAX_TOUCH_POINTS))
1765 {
1766 // Touch has ended for this point
1767 CORE.Input.Touch.position[platform.touchSlot].x = -1;
1768 CORE.Input.Touch.position[platform.touchSlot].y = -1;
1769 }
1770 }
1771
1772 // Touchscreen tap
1773 if (event.code == ABS_PRESSURE)
1774 {
1775 int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
1776
1777 if (!event.value && previousMouseLeftButtonState)
1778 {
1779 platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
1780 touchAction = 0; // TOUCH_ACTION_UP
1781 }
1782
1783 if (event.value && !previousMouseLeftButtonState)
1784 {
1785 platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
1786 touchAction = 1; // TOUCH_ACTION_DOWN
1787 }
1788 }
1789
1790 }
1791
1792 // Button parsing
1793 if (event.type == EV_KEY)
1794 {
1795 // Mouse button parsing
1796 if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
1797 {
1798 platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
1799
1800 if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN
1801 else touchAction = 0; // TOUCH_ACTION_UP
1802 }
1803
1804 if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
1805 if (event.code == BTN_MIDDLE) platform.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value;
1806 if (event.code == BTN_SIDE) platform.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value;
1807 if (event.code == BTN_EXTRA) platform.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value;
1808 if (event.code == BTN_FORWARD) platform.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value;
1809 if (event.code == BTN_BACK) platform.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value;
1810 }
1811
1812 // Screen confinement
1813 if (!CORE.Input.Mouse.cursorHidden)
1814 {
1815 if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
1816 if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x;
1817
1818 if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
1819 if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y;
1820 }
1821
1822 // Update touch point count
1823 CORE.Input.Touch.pointCount = 0;
1824 for (int i = 0; i < MAX_TOUCH_POINTS; i++)
1825 {
1826 if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++;
1827 }
1828
1829#if defined(SUPPORT_GESTURES_SYSTEM)
1830 if (touchAction > -1)
1831 {
1832 GestureEvent gestureEvent = { 0 };
1833
1834 gestureEvent.touchAction = touchAction;
1835 gestureEvent.pointCount = CORE.Input.Touch.pointCount;
1836
1837 for (int i = 0; i < MAX_TOUCH_POINTS; i++)
1838 {
1839 gestureEvent.pointId[i] = i;
1840 gestureEvent.position[i] = CORE.Input.Touch.position[i];
1841 }
1842
1843 ProcessGestureEvent(gestureEvent);
1844
1845 touchAction = -1;
1846 }
1847#endif
1848 }
1849}
1850
1851// Search matching DRM mode in connector's mode list
1852static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode)
1853{
1854 if (NULL == connector) return -1;
1855 if (NULL == mode) return -1;
1856
1857 // safe bitwise comparison of two modes
1858 #define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b))? sizeof(a) : sizeof(b))
1859
1860 for (size_t i = 0; i < connector->count_modes; i++)
1861 {
1862 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
1863 connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
1864
1865 if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i;
1866 }
1867
1868 return -1;
1869
1870 #undef BINCMP
1871}
1872
1873// Search exactly matching DRM connector mode in connector's list
1874static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
1875{
1876 TRACELOG(LOG_TRACE, "DISPLAY: Searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no");
1877
1878 if (NULL == connector) return -1;
1879
1880 for (int i = 0; i < platform.connector->count_modes; i++)
1881 {
1882 const drmModeModeInfo *const mode = &platform.connector->modes[i];
1883
1884 TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
1885
1886 if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) continue;
1887
1888 if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i;
1889 }
1890
1891 TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found");
1892 return -1;
1893}
1894
1895// Search the nearest matching DRM connector mode in connector's list
1896static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
1897{
1898 TRACELOG(LOG_TRACE, "DISPLAY: Searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no");
1899
1900 if (NULL == connector) return -1;
1901
1902 int nearestIndex = -1;
1903 for (int i = 0; i < platform.connector->count_modes; i++)
1904 {
1905 const drmModeModeInfo *const mode = &platform.connector->modes[i];
1906
1907 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
1908 (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
1909
1910 if ((mode->hdisplay < width) || (mode->vdisplay < height))
1911 {
1912 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small");
1913 continue;
1914 }
1915
1916 if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced))
1917 {
1918 TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode");
1919 continue;
1920 }
1921
1922 if (nearestIndex < 0)
1923 {
1924 nearestIndex = i;
1925 continue;
1926 }
1927
1928 const int widthDiff = abs(mode->hdisplay - width);
1929 const int heightDiff = abs(mode->vdisplay - height);
1930 const int fpsDiff = abs(mode->vrefresh - fps);
1931
1932 const int nearestWidthDiff = abs(platform.connector->modes[nearestIndex].hdisplay - width);
1933 const int nearestHeightDiff = abs(platform.connector->modes[nearestIndex].vdisplay - height);
1934 const int nearestFpsDiff = abs(platform.connector->modes[nearestIndex].vrefresh - fps);
1935
1936 if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) nearestIndex = i;
1937 }
1938
1939 return nearestIndex;
1940}
1941
1942// EOF
diff --git a/raylib/src/platforms/rcore_template.c b/raylib/src/platforms/rcore_template.c
new file mode 100644
index 0000000..6bc3432
--- /dev/null
+++ b/raylib/src/platforms/rcore_template.c
@@ -0,0 +1,597 @@
1/**********************************************************************************************
2*
3* rcore_<platform> template - Functions to manage window, graphics device and inputs
4*
5* PLATFORM: <PLATFORM>
6* - TODO: Define the target platform for the core
7*
8* LIMITATIONS:
9* - Limitation 01
10* - Limitation 02
11*
12* POSSIBLE IMPROVEMENTS:
13* - Improvement 01
14* - Improvement 02
15*
16* ADDITIONAL NOTES:
17* - TRACELOG() function is located in raylib [utils] module
18*
19* CONFIGURATION:
20* #define RCORE_PLATFORM_CUSTOM_FLAG
21* Custom flag for rcore on target platform -not used-
22*
23* DEPENDENCIES:
24* - <platform-specific SDK dependency>
25* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
26*
27*
28* LICENSE: zlib/libpng
29*
30* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
31*
32* This software is provided "as-is", without any express or implied warranty. In no event
33* will the authors be held liable for any damages arising from the use of this software.
34*
35* Permission is granted to anyone to use this software for any purpose, including commercial
36* applications, and to alter it and redistribute it freely, subject to the following restrictions:
37*
38* 1. The origin of this software must not be misrepresented; you must not claim that you
39* wrote the original software. If you use this software in a product, an acknowledgment
40* in the product documentation would be appreciated but is not required.
41*
42* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
43* as being the original software.
44*
45* 3. This notice may not be removed or altered from any source distribution.
46*
47**********************************************************************************************/
48
49// TODO: Include the platform specific libraries
50
51//----------------------------------------------------------------------------------
52// Types and Structures Definition
53//----------------------------------------------------------------------------------
54typedef struct {
55 // TODO: Define the platform specific variables required
56
57 // Display data
58 EGLDisplay device; // Native display device (physical screen connection)
59 EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
60 EGLContext context; // Graphic context, mode in which drawing can be done
61 EGLConfig config; // Graphic config
62} PlatformData;
63
64//----------------------------------------------------------------------------------
65// Global Variables Definition
66//----------------------------------------------------------------------------------
67extern CoreData CORE; // Global CORE state context
68
69static PlatformData platform = { 0 }; // Platform specific data
70
71//----------------------------------------------------------------------------------
72// Module Internal Functions Declaration
73//----------------------------------------------------------------------------------
74int InitPlatform(void); // Initialize platform (graphics, inputs and more)
75bool InitGraphicsDevice(void); // Initialize graphics device
76
77//----------------------------------------------------------------------------------
78// Module Functions Declaration
79//----------------------------------------------------------------------------------
80// NOTE: Functions declaration is provided by raylib.h
81
82//----------------------------------------------------------------------------------
83// Module Functions Definition: Window and Graphics Device
84//----------------------------------------------------------------------------------
85
86// Check if application should close
87bool WindowShouldClose(void)
88{
89 if (CORE.Window.ready) return CORE.Window.shouldClose;
90 else return true;
91}
92
93// Toggle fullscreen mode
94void ToggleFullscreen(void)
95{
96 TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
97}
98
99// Toggle borderless windowed mode
100void ToggleBorderlessWindowed(void)
101{
102 TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
103}
104
105// Set window state: maximized, if resizable
106void MaximizeWindow(void)
107{
108 TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
109}
110
111// Set window state: minimized
112void MinimizeWindow(void)
113{
114 TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
115}
116
117// Set window state: not minimized/maximized
118void RestoreWindow(void)
119{
120 TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
121}
122
123// Set window configuration state using flags
124void SetWindowState(unsigned int flags)
125{
126 TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
127}
128
129// Clear window configuration state flags
130void ClearWindowState(unsigned int flags)
131{
132 TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
133}
134
135// Set icon for window
136void SetWindowIcon(Image image)
137{
138 TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
139}
140
141// Set icon for window
142void SetWindowIcons(Image *images, int count)
143{
144 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
145}
146
147// Set title for window
148void SetWindowTitle(const char *title)
149{
150 CORE.Window.title = title;
151}
152
153// Set window position on screen (windowed mode)
154void SetWindowPosition(int x, int y)
155{
156 TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
157}
158
159// Set monitor for the current window
160void SetWindowMonitor(int monitor)
161{
162 TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
163}
164
165// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
166void SetWindowMinSize(int width, int height)
167{
168 CORE.Window.screenMin.width = width;
169 CORE.Window.screenMin.height = height;
170}
171
172// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
173void SetWindowMaxSize(int width, int height)
174{
175 CORE.Window.screenMax.width = width;
176 CORE.Window.screenMax.height = height;
177}
178
179// Set window dimensions
180void SetWindowSize(int width, int height)
181{
182 TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
183}
184
185// Set window opacity, value opacity is between 0.0 and 1.0
186void SetWindowOpacity(float opacity)
187{
188 TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
189}
190
191// Set window focused
192void SetWindowFocused(void)
193{
194 TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
195}
196
197// Get native window handle
198void *GetWindowHandle(void)
199{
200 TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
201 return NULL;
202}
203
204// Get number of monitors
205int GetMonitorCount(void)
206{
207 TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
208 return 1;
209}
210
211// Get number of monitors
212int GetCurrentMonitor(void)
213{
214 TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
215 return 0;
216}
217
218// Get selected monitor position
219Vector2 GetMonitorPosition(int monitor)
220{
221 TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
222 return (Vector2){ 0, 0 };
223}
224
225// Get selected monitor width (currently used by monitor)
226int GetMonitorWidth(int monitor)
227{
228 TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform");
229 return 0;
230}
231
232// Get selected monitor height (currently used by monitor)
233int GetMonitorHeight(int monitor)
234{
235 TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform");
236 return 0;
237}
238
239// Get selected monitor physical width in millimetres
240int GetMonitorPhysicalWidth(int monitor)
241{
242 TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
243 return 0;
244}
245
246// Get selected monitor physical height in millimetres
247int GetMonitorPhysicalHeight(int monitor)
248{
249 TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
250 return 0;
251}
252
253// Get selected monitor refresh rate
254int GetMonitorRefreshRate(int monitor)
255{
256 TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
257 return 0;
258}
259
260// Get the human-readable, UTF-8 encoded name of the selected monitor
261const char *GetMonitorName(int monitor)
262{
263 TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform");
264 return "";
265}
266
267// Get window position XY on monitor
268Vector2 GetWindowPosition(void)
269{
270 TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform");
271 return (Vector2){ 0, 0 };
272}
273
274// Get window scale DPI factor for current monitor
275Vector2 GetWindowScaleDPI(void)
276{
277 TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
278 return (Vector2){ 1.0f, 1.0f };
279}
280
281// Set clipboard text content
282void SetClipboardText(const char *text)
283{
284 TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
285}
286
287// Get clipboard text content
288// NOTE: returned string is allocated and freed by GLFW
289const char *GetClipboardText(void)
290{
291 TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
292 return NULL;
293}
294
295// Show mouse cursor
296void ShowCursor(void)
297{
298 CORE.Input.Mouse.cursorHidden = false;
299}
300
301// Hides mouse cursor
302void HideCursor(void)
303{
304 CORE.Input.Mouse.cursorHidden = true;
305}
306
307// Enables cursor (unlock cursor)
308void EnableCursor(void)
309{
310 // Set cursor position in the middle
311 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
312
313 CORE.Input.Mouse.cursorHidden = false;
314}
315
316// Disables cursor (lock cursor)
317void DisableCursor(void)
318{
319 // Set cursor position in the middle
320 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
321
322 CORE.Input.Mouse.cursorHidden = true;
323}
324
325// Swap back buffer with front buffer (screen drawing)
326void SwapScreenBuffer(void)
327{
328 eglSwapBuffers(platform.device, platform.surface);
329}
330
331//----------------------------------------------------------------------------------
332// Module Functions Definition: Misc
333//----------------------------------------------------------------------------------
334
335// Get elapsed time measure in seconds since InitTimer()
336double GetTime(void)
337{
338 double time = 0.0;
339 struct timespec ts = { 0 };
340 clock_gettime(CLOCK_MONOTONIC, &ts);
341 unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
342
343 time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
344
345 return time;
346}
347
348// Open URL with default system browser (if available)
349// NOTE: This function is only safe to use if you control the URL given.
350// A user could craft a malicious string performing another action.
351// Only call this function yourself not with user input or make sure to check the string yourself.
352// Ref: https://github.com/raysan5/raylib/issues/686
353void OpenURL(const char *url)
354{
355 // Security check to (partially) avoid malicious code on target platform
356 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
357 else
358 {
359 // TODO:
360 }
361}
362
363//----------------------------------------------------------------------------------
364// Module Functions Definition: Inputs
365//----------------------------------------------------------------------------------
366
367// Set internal gamepad mappings
368int SetGamepadMappings(const char *mappings)
369{
370 TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
371 return 0;
372}
373
374// Set mouse position XY
375void SetMousePosition(int x, int y)
376{
377 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
378 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
379}
380
381// Set mouse cursor
382void SetMouseCursor(int cursor)
383{
384 TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
385}
386
387// Get physical key name.
388const char *GetKeyName(int key)
389{
390 TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
391 return "";
392}
393
394// Register all input events
395void PollInputEvents(void)
396{
397#if defined(SUPPORT_GESTURES_SYSTEM)
398 // NOTE: Gestures update must be called every frame to reset gestures correctly
399 // because ProcessGestureEvent() is just called on an event, not every frame
400 UpdateGestures();
401#endif
402
403 // Reset keys/chars pressed registered
404 CORE.Input.Keyboard.keyPressedQueueCount = 0;
405 CORE.Input.Keyboard.charPressedQueueCount = 0;
406
407 // Reset key repeats
408 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
409
410 // Reset last gamepad button/axis registered state
411 CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
412 //CORE.Input.Gamepad.axisCount = 0;
413
414 // Register previous touch states
415 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
416
417 // Reset touch positions
418 // TODO: It resets on target platform the mouse position and not filled again until a move-event,
419 // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
420 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
421
422 // Register previous keys states
423 // NOTE: Android supports up to 260 keys
424 for (int i = 0; i < 260; i++)
425 {
426 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
427 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
428 }
429
430 // TODO: Poll input events for current platform
431}
432
433//----------------------------------------------------------------------------------
434// Module Internal Functions Definition
435//----------------------------------------------------------------------------------
436
437// Initialize platform: graphics, inputs and more
438int InitPlatform(void)
439{
440 // TODO: Initialize graphic device: display/window
441 // It usually requires setting up the platform display system configuration
442 // and connexion with the GPU through some system graphic API
443 // raylib uses OpenGL so, platform should create that kind of connection
444 // Below example illustrates that process using EGL library
445 //----------------------------------------------------------------------------
446 CORE.Window.fullscreen = true;
447 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
448
449 EGLint samples = 0;
450 EGLint sampleBuffer = 0;
451 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
452 {
453 samples = 4;
454 sampleBuffer = 1;
455 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
456 }
457
458 const EGLint framebufferAttribs[] =
459 {
460 EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support
461 EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5)
462 EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6)
463 EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5)
464 //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
465 EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
466 //EGL_STENCIL_SIZE, 8, // Stencil buffer size
467 EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
468 EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
469 EGL_NONE
470 };
471
472 const EGLint contextAttribs[] =
473 {
474 EGL_CONTEXT_CLIENT_VERSION, 2,
475 EGL_NONE
476 };
477
478 EGLint numConfigs = 0;
479
480 // Get an EGL device connection
481 platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
482 if (platform.device == EGL_NO_DISPLAY)
483 {
484 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
485 return false;
486 }
487
488 // Initialize the EGL device connection
489 if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
490 {
491 // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
492 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
493 return false;
494 }
495
496 // Get an appropriate EGL framebuffer configuration
497 eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs);
498
499 // Set rendering API
500 eglBindAPI(EGL_OPENGL_ES_API);
501
502 // Create an EGL rendering context
503 platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
504 if (platform.context == EGL_NO_CONTEXT)
505 {
506 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
507 return -1;
508 }
509
510 // Create an EGL window surface
511 EGLint displayFormat = 0;
512
513 // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
514 // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
515 eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
516
517 // Android specific call
518 ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size
519
520 platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL);
521
522 // There must be at least one frame displayed before the buffers are swapped
523 eglSwapInterval(platform.device, 1);
524
525 EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
526
527 // Check surface and context activation
528 if (result != EGL_FALSE)
529 {
530 CORE.Window.ready = true;
531
532 CORE.Window.render.width = CORE.Window.screen.width;
533 CORE.Window.render.height = CORE.Window.screen.height;
534 CORE.Window.currentFbo.width = CORE.Window.render.width;
535 CORE.Window.currentFbo.height = CORE.Window.render.height;
536
537 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
538 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
539 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
540 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
541 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
542 }
543 else
544 {
545 TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
546 return -1;
547 }
548 //----------------------------------------------------------------------------
549
550 // If everything work as expected, we can continue
551 CORE.Window.render.width = CORE.Window.screen.width;
552 CORE.Window.render.height = CORE.Window.screen.height;
553 CORE.Window.currentFbo.width = CORE.Window.render.width;
554 CORE.Window.currentFbo.height = CORE.Window.render.height;
555
556 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
557 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
558 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
559 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
560 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
561
562 // TODO: Load OpenGL extensions
563 // NOTE: GL procedures address loader is required to load extensions
564 //----------------------------------------------------------------------------
565 rlLoadExtensions(eglGetProcAddress);
566 //----------------------------------------------------------------------------
567
568 // TODO: Initialize input events system
569 // It could imply keyboard, mouse, gamepad, touch...
570 // Depending on the platform libraries/SDK it could use a callback mechanism
571 // For system events and inputs evens polling on a per-frame basis, use PollInputEvents()
572 //----------------------------------------------------------------------------
573 // ...
574 //----------------------------------------------------------------------------
575
576 // TODO: Initialize timing system
577 //----------------------------------------------------------------------------
578 InitTimer();
579 //----------------------------------------------------------------------------
580
581 // TODO: Initialize storage system
582 //----------------------------------------------------------------------------
583 CORE.Storage.basePath = GetWorkingDirectory();
584 //----------------------------------------------------------------------------
585
586 TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully");
587
588 return 0;
589}
590
591// Close platform
592void ClosePlatform(void)
593{
594 // TODO: De-initialize graphics, inputs and more
595}
596
597// EOF
diff --git a/raylib/src/platforms/rcore_web.c b/raylib/src/platforms/rcore_web.c
new file mode 100644
index 0000000..f9d93e5
--- /dev/null
+++ b/raylib/src/platforms/rcore_web.c
@@ -0,0 +1,1792 @@
1/**********************************************************************************************
2*
3* rcore_web - Functions to manage window, graphics device and inputs
4*
5* PLATFORM: WEB
6* - HTML5 (WebAssembly)
7*
8* LIMITATIONS:
9* - Limitation 01
10* - Limitation 02
11*
12* POSSIBLE IMPROVEMENTS:
13* - Replace glfw3 dependency by direct browser API calls (same as library_glfw3.js)
14*
15* ADDITIONAL NOTES:
16* - TRACELOG() function is located in raylib [utils] module
17*
18* CONFIGURATION:
19* #define RCORE_PLATFORM_CUSTOM_FLAG
20* Custom flag for rcore on target platform -not used-
21*
22* DEPENDENCIES:
23* - emscripten: Allow interaction between browser API and C
24* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
25*
26*
27* LICENSE: zlib/libpng
28*
29* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
30*
31* This software is provided "as-is", without any express or implied warranty. In no event
32* will the authors be held liable for any damages arising from the use of this software.
33*
34* Permission is granted to anyone to use this software for any purpose, including commercial
35* applications, and to alter it and redistribute it freely, subject to the following restrictions:
36*
37* 1. The origin of this software must not be misrepresented; you must not claim that you
38* wrote the original software. If you use this software in a product, an acknowledgment
39* in the product documentation would be appreciated but is not required.
40*
41* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
42* as being the original software.
43*
44* 3. This notice may not be removed or altered from any source distribution.
45*
46**********************************************************************************************/
47
48#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3
49 // NOTE: Already provided by rlgl implementation (on glad.h)
50#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management
51
52#include <emscripten/emscripten.h> // Emscripten functionality for C
53#include <emscripten/html5.h> // Emscripten HTML5 library
54
55#include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
56
57//----------------------------------------------------------------------------------
58// Defines and Macros
59//----------------------------------------------------------------------------------
60// TODO: HACK: Added flag if not provided by GLFW when using external library
61// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev
62#if !defined(GLFW_MOUSE_PASSTHROUGH)
63 #define GLFW_MOUSE_PASSTHROUGH 0x0002000D
64#endif
65
66#if (_POSIX_C_SOURCE < 199309L)
67 #undef _POSIX_C_SOURCE
68 #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
69#endif
70
71//----------------------------------------------------------------------------------
72// Types and Structures Definition
73//----------------------------------------------------------------------------------
74typedef struct {
75 GLFWwindow *handle; // GLFW window handle (graphic device)
76 bool ourFullscreen; // Internal var to filter our handling of fullscreen vs the user handling of fullscreen
77 int unmaximizedWidth; // Internal var to store the unmaximized window (canvas) width
78 int unmaximizedHeight; // Internal var to store the unmaximized window (canvas) height
79} PlatformData;
80
81//----------------------------------------------------------------------------------
82// Global Variables Definition
83//----------------------------------------------------------------------------------
84extern CoreData CORE; // Global CORE state context
85
86static PlatformData platform = { 0 }; // Platform specific data
87
88//----------------------------------------------------------------------------------
89// Local Variables Definition
90//----------------------------------------------------------------------------------
91static const char cursorLUT[11][12] = {
92 "default", // 0 MOUSE_CURSOR_DEFAULT
93 "default", // 1 MOUSE_CURSOR_ARROW
94 "text", // 2 MOUSE_CURSOR_IBEAM
95 "crosshair", // 3 MOUSE_CURSOR_CROSSHAIR
96 "pointer", // 4 MOUSE_CURSOR_POINTING_HAND
97 "ew-resize", // 5 MOUSE_CURSOR_RESIZE_EW
98 "ns-resize", // 6 MOUSE_CURSOR_RESIZE_NS
99 "nwse-resize", // 7 MOUSE_CURSOR_RESIZE_NWSE
100 "nesw-resize", // 8 MOUSE_CURSOR_RESIZE_NESW
101 "move", // 9 MOUSE_CURSOR_RESIZE_ALL
102 "not-allowed" // 10 MOUSE_CURSOR_NOT_ALLOWED
103};
104
105Vector2 lockedMousePos = { 0 };
106
107//----------------------------------------------------------------------------------
108// Module Internal Functions Declaration
109//----------------------------------------------------------------------------------
110int InitPlatform(void); // Initialize platform (graphics, inputs and more)
111void ClosePlatform(void); // Close platform
112
113// Error callback event
114static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
115
116// Window callbacks events
117static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized
118static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored
119//static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized
120static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus
121static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
122static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale
123
124// Input callbacks events
125static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
126static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value)
127static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed
128static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move
129static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel
130static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
131
132// Emscripten window callback events
133static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
134// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
135static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
136
137// Emscripten input callback events
138static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
139static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
140static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData);
141static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
142static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
143
144//----------------------------------------------------------------------------------
145// Module Functions Declaration
146//----------------------------------------------------------------------------------
147// NOTE: Functions declaration is provided by raylib.h
148
149//----------------------------------------------------------------------------------
150// Module Functions Definition: Window and Graphics Device
151//----------------------------------------------------------------------------------
152
153// Check if application should close
154bool WindowShouldClose(void)
155{
156 // Emterpreter-Async required to run sync code
157 // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
158 // By default, this function is never called on a web-ready raylib example because we encapsulate
159 // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously
160 // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter!
161 emscripten_sleep(16);
162 return false;
163}
164
165// Toggle fullscreen mode
166void ToggleFullscreen(void)
167{
168 platform.ourFullscreen = true;
169 bool enterFullscreen = false;
170
171 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
172 if (wasFullscreen)
173 {
174 if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterFullscreen = false;
175 else if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterFullscreen = true;
176 else
177 {
178 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
179 const int canvasStyleWidth = EM_ASM_INT( { return parseInt(document.getElementById('canvas').style.width); }, 0);
180 if (canvasStyleWidth > canvasWidth) enterFullscreen = false;
181 else enterFullscreen = true;
182 }
183
184 EM_ASM(document.exitFullscreen(););
185
186 CORE.Window.fullscreen = false;
187 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
188 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
189 }
190 else enterFullscreen = true;
191
192 if (enterFullscreen)
193 {
194 // NOTE: The setTimeouts handle the browser mode change delay
195 EM_ASM
196 (
197 setTimeout(function()
198 {
199 Module.requestFullscreen(false, false);
200 }, 100);
201 );
202 CORE.Window.fullscreen = true;
203 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
204 }
205
206 // NOTE: Old notes below:
207 /*
208 EM_ASM
209 (
210 // This strategy works well while using raylib minimal web shell for emscripten,
211 // it re-scales the canvas to fullscreen using monitor resolution, for tools this
212 // is a good strategy but maybe games prefer to keep current canvas resolution and
213 // display it in fullscreen, adjusting monitor resolution if possible
214 if (document.fullscreenElement) document.exitFullscreen();
215 else Module.requestFullscreen(true, true); //false, true);
216 );
217 */
218 // EM_ASM(Module.requestFullscreen(false, false););
219 /*
220 if (!CORE.Window.fullscreen)
221 {
222 // Option 1: Request fullscreen for the canvas element
223 // This option does not seem to work at all:
224 // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security,
225 // the user must click once on the canvas to hide the pointer or transition to full screen
226 //emscripten_request_fullscreen("#canvas", false);
227
228 // Option 2: Request fullscreen for the canvas element with strategy
229 // This option does not seem to work at all
230 // Ref: https://github.com/emscripten-core/emscripten/issues/5124
231 // EmscriptenFullscreenStrategy strategy = {
232 // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
233 // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
234 // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
235 // .canvasResizedCallback = EmscriptenWindowResizedCallback,
236 // .canvasResizedCallbackUserData = NULL
237 // };
238 //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy);
239
240 // Option 3: Request fullscreen for the canvas element with strategy
241 // It works as expected but only inside the browser (client area)
242 EmscriptenFullscreenStrategy strategy = {
243 .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
244 .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
245 .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
246 .canvasResizedCallback = EmscriptenWindowResizedCallback,
247 .canvasResizedCallbackUserData = NULL
248 };
249 emscripten_enter_soft_fullscreen("#canvas", &strategy);
250
251 int width, height;
252 emscripten_get_canvas_element_size("#canvas", &width, &height);
253 TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height);
254
255 CORE.Window.fullscreen = true; // Toggle fullscreen flag
256 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
257 }
258 else
259 {
260 //emscripten_exit_fullscreen();
261 //emscripten_exit_soft_fullscreen();
262
263 int width, height;
264 emscripten_get_canvas_element_size("#canvas", &width, &height);
265 TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height);
266
267 CORE.Window.fullscreen = false; // Toggle fullscreen flag
268 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
269 }
270 */
271}
272
273// Toggle borderless windowed mode
274void ToggleBorderlessWindowed(void)
275{
276 platform.ourFullscreen = true;
277 bool enterBorderless = false;
278
279 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
280 if (wasFullscreen)
281 {
282 if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterBorderless = false;
283 else if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterBorderless = true;
284 else
285 {
286 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
287 const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
288 if (screenWidth == canvasWidth) enterBorderless = false;
289 else enterBorderless = true;
290 }
291
292 EM_ASM(document.exitFullscreen(););
293
294 CORE.Window.fullscreen = false;
295 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
296 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
297 }
298 else enterBorderless = true;
299
300 if (enterBorderless)
301 {
302 // NOTE: 1. The setTimeouts handle the browser mode change delay
303 // 2. The style unset handles the possibility of a width="value%" like on the default shell.html file
304 EM_ASM
305 (
306 setTimeout(function()
307 {
308 Module.requestFullscreen(false, true);
309 setTimeout(function()
310 {
311 canvas.style.width="unset";
312 }, 100);
313 }, 100);
314 );
315 CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
316 }
317}
318
319// Set window state: maximized, if resizable
320void MaximizeWindow(void)
321{
322 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE && !(CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
323 {
324 platform.unmaximizedWidth = CORE.Window.screen.width;
325 platform.unmaximizedHeight = CORE.Window.screen.height;
326
327 const int tabWidth = EM_ASM_INT( return window.innerWidth; );
328 const int tabHeight = EM_ASM_INT( return window.innerHeight; );
329
330 if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
331
332 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
333 }
334}
335
336// Set window state: minimized
337void MinimizeWindow(void)
338{
339 TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
340}
341
342// Set window state: not minimized/maximized
343void RestoreWindow(void)
344{
345 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE && (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
346 {
347 if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
348
349 CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
350 }
351}
352
353// Set window configuration state using flags
354void SetWindowState(unsigned int flags)
355{
356 // Check previous state and requested state to apply required changes
357 // NOTE: In most cases the functions already change the flags internally
358
359 // State change: FLAG_VSYNC_HINT
360 if ((flags & FLAG_VSYNC_HINT) > 0)
361 {
362 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_VSYNC_HINT) not available on target platform");
363 }
364
365 // State change: FLAG_BORDERLESS_WINDOWED_MODE
366 if ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
367 {
368 // NOTE: Window state flag updated inside ToggleBorderlessWindowed() function
369 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
370 if (wasFullscreen)
371 {
372 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
373 const int canvasStyleWidth = EM_ASM_INT( { return parseInt(document.getElementById('canvas').style.width); }, 0);
374 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed();
375 }
376 else ToggleBorderlessWindowed();
377 }
378
379 // State change: FLAG_FULLSCREEN_MODE
380 if ((flags & FLAG_FULLSCREEN_MODE) > 0)
381 {
382 // NOTE: Window state flag updated inside ToggleFullscreen() function
383 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
384 if (wasFullscreen)
385 {
386 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
387 const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
388 if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || screenWidth == canvasWidth ) ToggleFullscreen();
389 }
390 else ToggleFullscreen();
391 }
392
393 // State change: FLAG_WINDOW_RESIZABLE
394 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
395 {
396 glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
397 CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
398 }
399
400 // State change: FLAG_WINDOW_UNDECORATED
401 if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
402 {
403 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
404 }
405
406 // State change: FLAG_WINDOW_HIDDEN
407 if ((flags & FLAG_WINDOW_HIDDEN) > 0)
408 {
409 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
410 }
411
412 // State change: FLAG_WINDOW_MINIMIZED
413 if ((flags & FLAG_WINDOW_MINIMIZED) > 0)
414 {
415 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
416 }
417
418 // State change: FLAG_WINDOW_MAXIMIZED
419 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
420 {
421 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
422 {
423 platform.unmaximizedWidth = CORE.Window.screen.width;
424 platform.unmaximizedHeight = CORE.Window.screen.height;
425
426 const int tabWidth = EM_ASM_INT( return window.innerWidth; );
427 const int tabHeight = EM_ASM_INT( return window.innerHeight; );
428
429 if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
430
431 CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
432 }
433 }
434
435 // State change: FLAG_WINDOW_UNFOCUSED
436 if ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
437 {
438 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
439 }
440
441 // State change: FLAG_WINDOW_TOPMOST
442 if ((flags & FLAG_WINDOW_TOPMOST) > 0)
443 {
444 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
445 }
446
447 // State change: FLAG_WINDOW_ALWAYS_RUN
448 if ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
449 {
450 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
451 }
452
453 // The following states can not be changed after window creation
454 // NOTE: Review for PLATFORM_WEB
455
456 // State change: FLAG_WINDOW_TRANSPARENT
457 if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
458 {
459 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
460 }
461
462 // State change: FLAG_WINDOW_HIGHDPI
463 if ((flags & FLAG_WINDOW_HIGHDPI) > 0)
464 {
465 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
466 }
467
468 // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
469 if ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
470 {
471 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
472 }
473
474 // State change: FLAG_MSAA_4X_HINT
475 if ((flags & FLAG_MSAA_4X_HINT) > 0)
476 {
477 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
478 }
479
480 // State change: FLAG_INTERLACED_HINT
481 if ((flags & FLAG_INTERLACED_HINT) > 0)
482 {
483 TRACELOG(LOG_WARNING, "SetWindowState(FLAG_INTERLACED_HINT) not available on target platform");
484 }
485}
486
487// Clear window configuration state flags
488void ClearWindowState(unsigned int flags)
489{
490 // Check previous state and requested state to apply required changes
491 // NOTE: In most cases the functions already change the flags internally
492
493 // State change: FLAG_VSYNC_HINT
494 if ((flags & FLAG_VSYNC_HINT) > 0)
495 {
496 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_VSYNC_HINT) not available on target platform");
497 }
498
499 // State change: FLAG_BORDERLESS_WINDOWED_MODE
500 if ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
501 {
502 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
503 if (wasFullscreen)
504 {
505 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
506 const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
507 if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen(););
508 }
509
510 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
511 }
512
513 // State change: FLAG_FULLSCREEN_MODE
514 if ((flags & FLAG_FULLSCREEN_MODE) > 0)
515 {
516 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
517 if (wasFullscreen)
518 {
519 const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
520 const int canvasStyleWidth = EM_ASM_INT( { return parseInt(document.getElementById('canvas').style.width); }, 0);
521 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen(););
522 }
523
524 CORE.Window.fullscreen = false;
525 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
526 }
527
528 // State change: FLAG_WINDOW_RESIZABLE
529 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
530 {
531 glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
532 CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
533 }
534
535 // State change: FLAG_WINDOW_HIDDEN
536 if ((flags & FLAG_WINDOW_HIDDEN) > 0)
537 {
538 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
539 }
540
541 // State change: FLAG_WINDOW_MINIMIZED
542 if ((flags & FLAG_WINDOW_MINIMIZED) > 0)
543 {
544 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
545 }
546
547 // State change: FLAG_WINDOW_MAXIMIZED
548 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
549 {
550 if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
551 {
552 if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
553
554 CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
555 }
556 }
557
558 // State change: FLAG_WINDOW_UNDECORATED
559 if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
560 {
561 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
562 }
563
564 // State change: FLAG_WINDOW_UNFOCUSED
565 if ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
566 {
567 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
568 }
569
570 // State change: FLAG_WINDOW_TOPMOST
571 if ((flags & FLAG_WINDOW_TOPMOST) > 0)
572 {
573 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
574 }
575
576 // State change: FLAG_WINDOW_ALWAYS_RUN
577 if ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
578 {
579 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
580 }
581
582 // The following states can not be changed after window creation
583 // NOTE: Review for PLATFORM_WEB
584
585 // State change: FLAG_WINDOW_TRANSPARENT
586 if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
587 {
588 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
589 }
590
591 // State change: FLAG_WINDOW_HIGHDPI
592 if ((flags & FLAG_WINDOW_HIGHDPI) > 0)
593 {
594 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
595 }
596
597 // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
598 if ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
599 {
600 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
601 }
602
603 // State change: FLAG_MSAA_4X_HINT
604 if ((flags & FLAG_MSAA_4X_HINT) > 0)
605 {
606 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
607 }
608
609 // State change: FLAG_INTERLACED_HINT
610 if ((flags & FLAG_INTERLACED_HINT) > 0)
611 {
612 TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_INTERLACED_HINT) not available on target platform");
613 }
614}
615
616// Set icon for window
617void SetWindowIcon(Image image)
618{
619 TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
620}
621
622// Set icon for window, multiple images
623void SetWindowIcons(Image *images, int count)
624{
625 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
626}
627
628// Set title for window
629void SetWindowTitle(const char *title)
630{
631 CORE.Window.title = title;
632 emscripten_set_window_title(title);
633}
634
635// Set window position on screen (windowed mode)
636void SetWindowPosition(int x, int y)
637{
638 TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
639}
640
641// Set monitor for the current window
642void SetWindowMonitor(int monitor)
643{
644 TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
645}
646
647// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
648void SetWindowMinSize(int width, int height)
649{
650 CORE.Window.screenMin.width = width;
651 CORE.Window.screenMin.height = height;
652
653 // Trigger the resize event once to update the window minimum width and height
654 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
655}
656
657// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
658void SetWindowMaxSize(int width, int height)
659{
660 CORE.Window.screenMax.width = width;
661 CORE.Window.screenMax.height = height;
662
663 // Trigger the resize event once to update the window maximum width and height
664 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
665}
666
667// Set window dimensions
668void SetWindowSize(int width, int height)
669{
670 glfwSetWindowSize(platform.handle, width, height);
671}
672
673// Set window opacity, value opacity is between 0.0 and 1.0
674void SetWindowOpacity(float opacity)
675{
676 if (opacity >= 1.0f) opacity = 1.0f;
677 else if (opacity <= 0.0f) opacity = 0.0f;
678 EM_ASM({ document.getElementById('canvas').style.opacity = $0; }, opacity);
679}
680
681// Set window focused
682void SetWindowFocused(void)
683{
684 TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
685}
686
687// Get native window handle
688void *GetWindowHandle(void)
689{
690 TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
691 return NULL;
692}
693
694// Get number of monitors
695int GetMonitorCount(void)
696{
697 TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
698 return 1;
699}
700
701// Get number of monitors
702int GetCurrentMonitor(void)
703{
704 TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
705 return 0;
706}
707
708// Get selected monitor position
709Vector2 GetMonitorPosition(int monitor)
710{
711 TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
712 return (Vector2){ 0, 0 };
713}
714
715// Get selected monitor width (currently used by monitor)
716int GetMonitorWidth(int monitor)
717{
718 // NOTE: Returned value is limited to the current monitor where the browser window is located
719 int width = 0;
720 width = EM_ASM_INT( { return screen.width; }, 0);
721 return width;
722}
723
724// Get selected monitor height (currently used by monitor)
725int GetMonitorHeight(int monitor)
726{
727 // NOTE: Returned value is limited to the current monitor where the browser window is located
728 int height = 0;
729 height = EM_ASM_INT( { return screen.height; }, 0);
730 return height;
731}
732
733// Get selected monitor physical width in millimetres
734int GetMonitorPhysicalWidth(int monitor)
735{
736 TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
737 return 0;
738}
739
740// Get selected monitor physical height in millimetres
741int GetMonitorPhysicalHeight(int monitor)
742{
743 TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
744 return 0;
745}
746
747// Get selected monitor refresh rate
748int GetMonitorRefreshRate(int monitor)
749{
750 TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
751 return 0;
752}
753
754// Get the human-readable, UTF-8 encoded name of the selected monitor
755const char *GetMonitorName(int monitor)
756{
757 TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform");
758 return "";
759}
760
761// Get window position XY on monitor
762Vector2 GetWindowPosition(void)
763{
764 // NOTE: Returned position is relative to the current monitor where the browser window is located
765 Vector2 position = { 0, 0 };
766 position.x = (float)EM_ASM_INT( { return window.screenX; }, 0);
767 position.y = (float)EM_ASM_INT( { return window.screenY; }, 0);
768 return position;
769}
770
771// Get window scale DPI factor for current monitor
772Vector2 GetWindowScaleDPI(void)
773{
774 TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
775 return (Vector2){ 1.0f, 1.0f };
776}
777
778// Set clipboard text content
779void SetClipboardText(const char *text)
780{
781 // Security check to (partially) avoid malicious code
782 if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character");
783 else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
784}
785
786// Get clipboard text content
787// NOTE: returned string is allocated and freed by GLFW
788const char *GetClipboardText(void)
789{
790/*
791 // Accessing clipboard data from browser is tricky due to security reasons
792 // The method to use is navigator.clipboard.readText() but this is an asynchronous method
793 // that will return at some moment after the function is called with the required data
794 emscripten_run_script_string("navigator.clipboard.readText() \
795 .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \
796 .catch(err => { console.error('Failed to read clipboard contents: ', err); });"
797 );
798
799 // The main issue is getting that data, one approach could be using ASYNCIFY and wait
800 // for the data but it requires adding Asyncify emscripten library on compilation
801
802 // Another approach could be just copy the data in a HTML text field and try to retrieve it
803 // later on if available... and clean it for future accesses
804*/
805 return NULL;
806}
807
808// Show mouse cursor
809void ShowCursor(void)
810{
811 if (CORE.Input.Mouse.cursorHidden)
812 {
813 EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursorLUT[CORE.Input.Mouse.cursor]);
814
815 CORE.Input.Mouse.cursorHidden = false;
816 }
817}
818
819// Hides mouse cursor
820void HideCursor(void)
821{
822 if (!CORE.Input.Mouse.cursorHidden)
823 {
824 EM_ASM(document.getElementById('canvas').style.cursor = 'none';);
825
826 CORE.Input.Mouse.cursorHidden = true;
827 }
828}
829
830// Enables cursor (unlock cursor)
831void EnableCursor(void)
832{
833 emscripten_exit_pointerlock();
834
835 // Set cursor position in the middle
836 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
837
838 // NOTE: CORE.Input.Mouse.cursorHidden handled by EmscriptenPointerlockCallback()
839}
840
841// Disables cursor (lock cursor)
842void DisableCursor(void)
843{
844 // TODO: figure out how not to hard code the canvas ID here.
845 emscripten_request_pointerlock("#canvas", 1);
846
847 // Set cursor position in the middle
848 SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
849
850 // NOTE: CORE.Input.Mouse.cursorHidden handled by EmscriptenPointerlockCallback()
851}
852
853// Swap back buffer with front buffer (screen drawing)
854void SwapScreenBuffer(void)
855{
856 glfwSwapBuffers(platform.handle);
857}
858
859//----------------------------------------------------------------------------------
860// Module Functions Definition: Misc
861//----------------------------------------------------------------------------------
862
863// Get elapsed time measure in seconds since InitTimer()
864double GetTime(void)
865{
866 double time = glfwGetTime(); // Elapsed time since glfwInit()
867 return time;
868}
869
870// Open URL with default system browser (if available)
871// NOTE: This function is only safe to use if you control the URL given.
872// A user could craft a malicious string performing another action.
873// Only call this function yourself not with user input or make sure to check the string yourself.
874// Ref: https://github.com/raysan5/raylib/issues/686
875void OpenURL(const char *url)
876{
877 // Security check to (partially) avoid malicious code on target platform
878 if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
879 else emscripten_run_script(TextFormat("window.open('%s', '_blank')", url));
880}
881
882//----------------------------------------------------------------------------------
883// Module Functions Definition: Inputs
884//----------------------------------------------------------------------------------
885
886// Set internal gamepad mappings
887int SetGamepadMappings(const char *mappings)
888{
889 TRACELOG(LOG_INFO, "SetGamepadMappings not implemented in rcore_web.c");
890
891 return 0;
892}
893
894// Set gamepad vibration
895void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
896{
897 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (duration > 0.0f))
898 {
899 if (leftMotor < 0.0f) leftMotor = 0.0f;
900 if (leftMotor > 1.0f) leftMotor = 1.0f;
901 if (rightMotor < 0.0f) rightMotor = 0.0f;
902 if (rightMotor > 1.0f) rightMotor = 1.0f;
903 if (duration > MAX_GAMEPAD_VIBRATION_TIME) duration = MAX_GAMEPAD_VIBRATION_TIME;
904 duration *= 1000.0f; // Convert duration to ms
905
906 // Note: At the moment (2024.10.21) Chrome, Edge, Opera, Safari, Android Chrome, Android Webview only support the vibrationActuator API,
907 // and Firefox only supports the hapticActuators API
908 EM_ASM({
909 try
910 {
911 navigator.getGamepads()[$0].vibrationActuator.playEffect('dual-rumble', { startDelay: 0, duration: $3, weakMagnitude: $1, strongMagnitude: $2 });
912 }
913 catch (e)
914 {
915 try
916 {
917 navigator.getGamepads()[$0].hapticActuators[0].pulse($2, $3);
918 }
919 catch (e) { }
920 }
921 }, gamepad, leftMotor, rightMotor, duration);
922 }
923}
924
925// Set mouse position XY
926void SetMousePosition(int x, int y)
927{
928 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
929 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
930
931 if (CORE.Input.Mouse.cursorHidden) lockedMousePos = CORE.Input.Mouse.currentPosition;
932
933 // NOTE: emscripten not implemented
934 glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
935}
936
937// Set mouse cursor
938void SetMouseCursor(int cursor)
939{
940 if (CORE.Input.Mouse.cursor != cursor)
941 {
942 if (!CORE.Input.Mouse.cursorHidden) EM_ASM( { document.getElementById('canvas').style.cursor = UTF8ToString($0); }, cursorLUT[cursor]);
943
944 CORE.Input.Mouse.cursor = cursor;
945 }
946}
947
948// Get physical key name.
949const char *GetKeyName(int key)
950{
951 TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
952 return "";
953}
954
955// Register all input events
956void PollInputEvents(void)
957{
958#if defined(SUPPORT_GESTURES_SYSTEM)
959 // NOTE: Gestures update must be called every frame to reset gestures correctly
960 // because ProcessGestureEvent() is just called on an event, not every frame
961 UpdateGestures();
962#endif
963
964 // Reset keys/chars pressed registered
965 CORE.Input.Keyboard.keyPressedQueueCount = 0;
966 CORE.Input.Keyboard.charPressedQueueCount = 0;
967
968 // Reset last gamepad button/axis registered state
969 CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
970 //CORE.Input.Gamepad.axisCount = 0;
971
972 // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback)
973
974 // Register previous keys states
975 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
976 {
977 CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
978 CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
979 }
980
981 // Register previous mouse states
982 for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
983
984 // Register previous mouse wheel state
985 CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
986 CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
987
988 // Register previous mouse position
989 CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
990
991 // Register previous touch states
992 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
993
994 // Reset touch positions
995 // TODO: It resets on target platform the mouse position and not filled again until a move-event,
996 // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
997 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
998
999 // Gamepad support using emscripten API
1000 // NOTE: GLFW3 joystick functionality not available in web
1001
1002 // Get number of gamepads connected
1003 int numGamepads = 0;
1004 if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads();
1005
1006 for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++)
1007 {
1008 // Register previous gamepad button states
1009 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
1010
1011 EmscriptenGamepadEvent gamepadState;
1012
1013 int result = emscripten_get_gamepad_status(i, &gamepadState);
1014
1015 if (result == EMSCRIPTEN_RESULT_SUCCESS)
1016 {
1017 // Register buttons data for every connected gamepad
1018 for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++)
1019 {
1020 GamepadButton button = -1;
1021
1022 // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface
1023 switch (j)
1024 {
1025 case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
1026 case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
1027 case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
1028 case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
1029 case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
1030 case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
1031 case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
1032 case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
1033 case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
1034 case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
1035 case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
1036 case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
1037 case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
1038 case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
1039 case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
1040 case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
1041 default: break;
1042 }
1043
1044 if (button + 1 != 0) // Check for valid button
1045 {
1046 if (gamepadState.digitalButton[j] == 1)
1047 {
1048 CORE.Input.Gamepad.currentButtonState[i][button] = 1;
1049 CORE.Input.Gamepad.lastButtonPressed = button;
1050 }
1051 else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
1052 }
1053
1054 //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
1055 }
1056
1057 // Register axis data for every connected gamepad
1058 for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++)
1059 {
1060 CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j];
1061 }
1062
1063 CORE.Input.Gamepad.axisCount[i] = gamepadState.numAxes;
1064 }
1065 }
1066
1067 CORE.Window.resizedLastFrame = false;
1068
1069 // TODO: This code does not seem to do anything??
1070 //if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused)
1071 //else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) --> WARNING: Where is key input reset?
1072}
1073
1074//----------------------------------------------------------------------------------
1075// Module Internal Functions Definition
1076//----------------------------------------------------------------------------------
1077
1078// Initialize platform: graphics, inputs and more
1079int InitPlatform(void)
1080{
1081 glfwSetErrorCallback(ErrorCallback);
1082
1083 // Initialize GLFW internal global state
1084 int result = glfwInit();
1085 if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; }
1086
1087 // Initialize graphic device: display/window and graphic context
1088 //----------------------------------------------------------------------------
1089 glfwDefaultWindowHints(); // Set default windows hints
1090 // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits
1091 // glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits
1092 // glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits
1093 // glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits
1094 // glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits
1095 // glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window
1096 // glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
1097 // glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
1098
1099 // Check window creation flags
1100 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
1101
1102 if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
1103 else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden
1104
1105 if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
1106 else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window
1107
1108 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
1109 else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable
1110
1111 // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
1112 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
1113
1114 // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
1115 if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
1116
1117 if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
1118 else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
1119
1120 if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
1121 else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
1122
1123 // NOTE: Some GLFW flags are not supported on HTML5
1124 // e.g.: GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_MOUSE_PASSTHROUGH
1125
1126 // Scale content area based on the monitor content scale where window is placed on
1127 // NOTE: This feature requires emscripten 3.1.51
1128 //if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
1129 //else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
1130
1131 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
1132 {
1133 // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
1134 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
1135 glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0
1136 }
1137
1138 // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version
1139 // with backward compatibility to older OpenGL versions.
1140 // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context.
1141
1142 // Check selection OpenGL version
1143 if (rlGetVersion() == RL_OPENGL_21)
1144 {
1145 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint)
1146 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint)
1147 }
1148 else if (rlGetVersion() == RL_OPENGL_33)
1149 {
1150 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
1151 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
1152 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
1153 // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
1154 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above!
1155 // glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
1156 }
1157 else if (rlGetVersion() == RL_OPENGL_43)
1158 {
1159 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint)
1160 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
1161 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
1162 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE);
1163#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
1164 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context
1165#endif
1166 }
1167 else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context
1168 {
1169 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
1170 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
1171 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
1172 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
1173 }
1174 else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context
1175 {
1176 // TODO: It seems WebGL 2.0 context is not set despite being requested
1177 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
1178 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
1179 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
1180 glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
1181 }
1182
1183 // NOTE: Getting video modes is not implemented in emscripten GLFW3 version
1184 CORE.Window.display.width = CORE.Window.screen.width;
1185 CORE.Window.display.height = CORE.Window.screen.height;
1186
1187 // Init fullscreen toggle required var:
1188 platform.ourFullscreen = false;
1189
1190 if (CORE.Window.fullscreen)
1191 {
1192 // remember center for switchinging from fullscreen to window
1193 if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
1194 {
1195 // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
1196 // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
1197 CORE.Window.position.x = CORE.Window.display.width/4;
1198 CORE.Window.position.y = CORE.Window.display.height/4;
1199 }
1200 else
1201 {
1202 CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
1203 CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
1204 }
1205
1206 if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
1207 if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
1208
1209 // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor
1210 int count = 0;
1211 const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count);
1212
1213 // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height
1214 for (int i = 0; i < count; i++)
1215 {
1216 if ((unsigned int)modes[i].width >= CORE.Window.screen.width)
1217 {
1218 if ((unsigned int)modes[i].height >= CORE.Window.screen.height)
1219 {
1220 CORE.Window.display.width = modes[i].width;
1221 CORE.Window.display.height = modes[i].height;
1222 break;
1223 }
1224 }
1225 }
1226
1227 TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1228
1229 // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
1230 // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
1231 // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
1232 // by the sides to fit all monitor space...
1233
1234 // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
1235 // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
1236 // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
1237 // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
1238 // HighDPI monitors are properly considered in a following similar function: SetupViewport()
1239 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
1240
1241 platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
1242
1243 // NOTE: Full-screen change, not working properly...
1244 // glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
1245 }
1246 else
1247 {
1248 // No-fullscreen window creation
1249 platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
1250
1251 if (platform.handle)
1252 {
1253 CORE.Window.render.width = CORE.Window.screen.width;
1254 CORE.Window.render.height = CORE.Window.screen.height;
1255 }
1256 }
1257
1258 if (!platform.handle)
1259 {
1260 glfwTerminate();
1261 TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
1262 return -1;
1263 }
1264
1265 // WARNING: glfwCreateWindow() title doesn't work with emscripten
1266 emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " ");
1267
1268 // Set window callback events
1269 glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default!
1270 glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
1271 glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
1272 glfwSetDropCallback(platform.handle, WindowDropCallback);
1273
1274 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
1275 {
1276 glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
1277 }
1278
1279 // Set input callback events
1280 glfwSetKeyCallback(platform.handle, KeyCallback);
1281 glfwSetCharCallback(platform.handle, CharCallback);
1282 glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
1283 glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes
1284 glfwSetScrollCallback(platform.handle, MouseScrollCallback);
1285 glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
1286
1287 glfwMakeContextCurrent(platform.handle);
1288 result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web
1289
1290 // Check context activation
1291 if (result == true) //(result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR))
1292 {
1293 CORE.Window.ready = true;
1294
1295 int fbWidth = CORE.Window.screen.width;
1296 int fbHeight = CORE.Window.screen.height;
1297
1298 CORE.Window.render.width = fbWidth;
1299 CORE.Window.render.height = fbHeight;
1300 CORE.Window.currentFbo.width = fbWidth;
1301 CORE.Window.currentFbo.height = fbHeight;
1302
1303 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
1304 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
1305 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
1306 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
1307 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
1308 }
1309 else
1310 {
1311 TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
1312 return -1;
1313 }
1314
1315 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
1316
1317 // If graphic device is no properly initialized, we end program
1318 if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
1319
1320 // Load OpenGL extensions
1321 // NOTE: GL procedures address loader is required to load extensions
1322 rlLoadExtensions(glfwGetProcAddress);
1323 //----------------------------------------------------------------------------
1324
1325 // Initialize input events callbacks
1326 //----------------------------------------------------------------------------
1327 // Setup callback functions for the DOM events
1328 emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenFullscreenChangeCallback);
1329
1330 // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
1331 // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
1332 // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
1333 // Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
1334 emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
1335
1336 // Trigger this once to get initial window sizing
1337 EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
1338
1339 // Support keyboard events -> Not used, GLFW.JS takes care of that
1340 // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
1341 // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
1342
1343 // Support mouse events
1344 emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
1345 emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback);
1346
1347 // Following the mouse delta when the mouse is locked
1348 emscripten_set_mousemove_callback("#canvas", NULL, 1, EmscriptenMouseMoveCallback);
1349
1350 // Support touch events
1351 emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
1352 emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
1353 emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
1354 emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
1355
1356 // Support gamepad events (not provided by GLFW3 on emscripten)
1357 emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
1358 emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
1359 //----------------------------------------------------------------------------
1360
1361 // Initialize timing system
1362 //----------------------------------------------------------------------------
1363 InitTimer();
1364 //----------------------------------------------------------------------------
1365
1366 // Initialize storage system
1367 //----------------------------------------------------------------------------
1368 CORE.Storage.basePath = GetWorkingDirectory();
1369 //----------------------------------------------------------------------------
1370
1371 TRACELOG(LOG_INFO, "PLATFORM: WEB: Initialized successfully");
1372
1373 return 0;
1374}
1375
1376// Close platform
1377void ClosePlatform(void)
1378{
1379 glfwDestroyWindow(platform.handle);
1380 glfwTerminate();
1381}
1382
1383// GLFW3 Error Callback, runs on GLFW3 error
1384static void ErrorCallback(int error, const char *description)
1385{
1386 TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
1387}
1388
1389// GLFW3 WindowSize Callback, runs when window is resizedLastFrame
1390// NOTE: Window resizing not allowed by default
1391static void WindowSizeCallback(GLFWwindow *window, int width, int height)
1392{
1393 // Reset viewport and projection matrix for new size
1394 SetupViewport(width, height);
1395
1396 CORE.Window.currentFbo.width = width;
1397 CORE.Window.currentFbo.height = height;
1398 CORE.Window.resizedLastFrame = true;
1399
1400 if (IsWindowFullscreen()) return;
1401
1402 // Set current screen size
1403 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
1404 {
1405 Vector2 windowScaleDPI = GetWindowScaleDPI();
1406
1407 CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x);
1408 CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y);
1409 }
1410 else
1411 {
1412 CORE.Window.screen.width = width;
1413 CORE.Window.screen.height = height;
1414 }
1415
1416 // NOTE: Postprocessing texture is not scaled to new size
1417}
1418
1419static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
1420{
1421 CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
1422}
1423
1424// GLFW3 WindowIconify Callback, runs when window is minimized/restored
1425static void WindowIconifyCallback(GLFWwindow *window, int iconified)
1426{
1427 if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
1428 else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
1429}
1430
1431/*
1432// GLFW3 Window Maximize Callback, runs when window is maximized
1433static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
1434{
1435 // TODO.
1436}
1437*/
1438
1439// GLFW3 WindowFocus Callback, runs when window get/lose focus
1440static void WindowFocusCallback(GLFWwindow *window, int focused)
1441{
1442 if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused
1443 else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus
1444}
1445
1446// GLFW3 Window Drop Callback, runs when drop files into window
1447static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
1448{
1449 if (count > 0)
1450 {
1451 // In case previous dropped filepaths have not been freed, we free them
1452 if (CORE.Window.dropFileCount > 0)
1453 {
1454 for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]);
1455
1456 RL_FREE(CORE.Window.dropFilepaths);
1457
1458 CORE.Window.dropFileCount = 0;
1459 CORE.Window.dropFilepaths = NULL;
1460 }
1461
1462 // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy
1463 CORE.Window.dropFileCount = count;
1464 CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *));
1465
1466 for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
1467 {
1468 CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
1469 strcpy(CORE.Window.dropFilepaths[i], paths[i]);
1470 }
1471 }
1472}
1473
1474// GLFW3 Keyboard Callback, runs on key pressed
1475static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
1476{
1477 if (key < 0) return; // Security check, macOS fn key generates -1
1478
1479 // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
1480 // to work properly with our implementation (IsKeyDown/IsKeyUp checks)
1481 if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
1482 else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
1483 else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
1484
1485 // Check if there is space available in the key queue
1486 if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS))
1487 {
1488 // Add character to the queue
1489 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
1490 CORE.Input.Keyboard.keyPressedQueueCount++;
1491 }
1492
1493 // Check the exit key to set close window
1494 if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
1495}
1496
1497// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value)
1498static void CharCallback(GLFWwindow *window, unsigned int key)
1499{
1500 //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
1501
1502 // NOTE: Registers any key down considering OS keyboard layout but
1503 // does not detect action events, those should be managed by user...
1504 // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
1505 // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
1506
1507 // Check if there is space available in the queue
1508 if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
1509 {
1510 // Add character to the queue
1511 CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key;
1512 CORE.Input.Keyboard.charPressedQueueCount++;
1513 }
1514}
1515
1516// GLFW3 Mouse Button Callback, runs on mouse button pressed
1517static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
1518{
1519 // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
1520 // but future releases may add more actions (i.e. GLFW_REPEAT)
1521 CORE.Input.Mouse.currentButtonState[button] = action;
1522 CORE.Input.Touch.currentTouchState[button] = action;
1523
1524#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
1525 // Process mouse events as touches to be able to use mouse-gestures
1526 GestureEvent gestureEvent = { 0 };
1527
1528 // Register touch actions
1529 if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
1530 else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP;
1531
1532 // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback()
1533
1534 // Assign a pointer ID
1535 gestureEvent.pointId[0] = 0;
1536
1537 // Register touch points count
1538 gestureEvent.pointCount = 1;
1539
1540 // Register touch points position, only one point registered
1541 gestureEvent.position[0] = GetMousePosition();
1542
1543 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
1544 gestureEvent.position[0].x /= (float)GetScreenWidth();
1545 gestureEvent.position[0].y /= (float)GetScreenHeight();
1546
1547 // Gesture data is sent to gestures-system for processing
1548 // Prevent calling ProcessGestureEvent() when Emscripten is present and there's a touch gesture, so EmscriptenTouchCallback() can handle it itself
1549 if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent);
1550
1551#endif
1552}
1553
1554// GLFW3 Cursor Position Callback, runs on mouse move
1555static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
1556{
1557 // If the pointer is not locked, follow the position
1558 if (!CORE.Input.Mouse.cursorHidden)
1559 {
1560 CORE.Input.Mouse.currentPosition.x = (float)x;
1561 CORE.Input.Mouse.currentPosition.y = (float)y;
1562 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
1563 }
1564
1565#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
1566 // Process mouse events as touches to be able to use mouse-gestures
1567 GestureEvent gestureEvent = { 0 };
1568
1569 gestureEvent.touchAction = TOUCH_ACTION_MOVE;
1570
1571 // Assign a pointer ID
1572 gestureEvent.pointId[0] = 0;
1573
1574 // Register touch points count
1575 gestureEvent.pointCount = 1;
1576
1577 // Register touch points position, only one point registered
1578 gestureEvent.position[0] = CORE.Input.Touch.position[0];
1579
1580 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
1581 gestureEvent.position[0].x /= (float)GetScreenWidth();
1582 gestureEvent.position[0].y /= (float)GetScreenHeight();
1583
1584 // Gesture data is sent to gestures-system for processing
1585 ProcessGestureEvent(gestureEvent);
1586#endif
1587}
1588
1589static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
1590{
1591 // To emulate the GLFW_RAW_MOUSE_MOTION property.
1592 if (CORE.Input.Mouse.cursorHidden)
1593 {
1594 CORE.Input.Mouse.previousPosition.x = lockedMousePos.x - mouseEvent->movementX;
1595 CORE.Input.Mouse.previousPosition.y = lockedMousePos.y - mouseEvent->movementY;
1596 }
1597
1598 return 1; // The event was consumed by the callback handler
1599}
1600
1601// GLFW3 Scrolling Callback, runs on mouse wheel
1602static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
1603{
1604 CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset };
1605}
1606
1607// GLFW3 CursorEnter Callback, when cursor enters the window
1608static void CursorEnterCallback(GLFWwindow *window, int enter)
1609{
1610 if (enter) CORE.Input.Mouse.cursorOnScreen = true;
1611 else CORE.Input.Mouse.cursorOnScreen = false;
1612}
1613
1614// Register fullscreen change events
1615static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData)
1616{
1617 // NOTE: 1. Reset the fullscreen flags if the user left fullscreen manually by pressing the Escape key
1618 // 2. Which is a necessary safeguard because that case will bypass the toggles CORE.Window.flags resets
1619 if (platform.ourFullscreen) platform.ourFullscreen = false;
1620 else
1621 {
1622 const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
1623 if (!wasFullscreen)
1624 {
1625 CORE.Window.fullscreen = false;
1626 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
1627 CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
1628 }
1629 }
1630
1631 return 1; // The event was consumed by the callback handler
1632}
1633
1634// Register window resize event
1635// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
1636// {
1637// // TODO: Implement EmscriptenWindowResizedCallback()?
1638
1639// return 1; // The event was consumed by the callback handler
1640// }
1641
1642// Register DOM element resize event
1643static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
1644{
1645 // Don't resize non-resizeable windows
1646 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1;
1647
1648 // This event is called whenever the window changes sizes,
1649 // so the size of the canvas object is explicitly retrieved below
1650 int width = EM_ASM_INT( return window.innerWidth; );
1651 int height = EM_ASM_INT( return window.innerHeight; );
1652
1653 if (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width;
1654 else if (width > (int)CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width;
1655
1656 if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height;
1657 else if (height > (int)CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height;
1658
1659 emscripten_set_canvas_element_size("#canvas", width, height);
1660
1661 SetupViewport(width, height); // Reset viewport and projection matrix for new size
1662
1663 CORE.Window.currentFbo.width = width;
1664 CORE.Window.currentFbo.height = height;
1665 CORE.Window.resizedLastFrame = true;
1666
1667 if (IsWindowFullscreen()) return 1;
1668
1669 // Set current screen size
1670 CORE.Window.screen.width = width;
1671 CORE.Window.screen.height = height;
1672
1673 // NOTE: Postprocessing texture is not scaled to new size
1674
1675 return 0;
1676}
1677
1678// Register mouse input events
1679static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
1680{
1681 // This is only for registering mouse click events with emscripten and doesn't need to do anything
1682
1683 return 1; // The event was consumed by the callback handler
1684}
1685
1686// Register pointer lock events
1687static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData)
1688{
1689 CORE.Input.Mouse.cursorHidden = EM_ASM_INT( { if (document.pointerLockElement) return 1; }, 0);
1690
1691 if (CORE.Input.Mouse.cursorHidden)
1692 {
1693 lockedMousePos = CORE.Input.Mouse.currentPosition;
1694 CORE.Input.Mouse.previousPosition = lockedMousePos;
1695 }
1696
1697 return 1; // The event was consumed by the callback handler
1698}
1699
1700// Register connected/disconnected gamepads events
1701static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
1702{
1703 /*
1704 TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"",
1705 eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
1706 gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
1707
1708 for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
1709 for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
1710 */
1711
1712 if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS))
1713 {
1714 CORE.Input.Gamepad.ready[gamepadEvent->index] = true;
1715 sprintf(CORE.Input.Gamepad.name[gamepadEvent->index], "%s", gamepadEvent->id);
1716 }
1717 else CORE.Input.Gamepad.ready[gamepadEvent->index] = false;
1718
1719 return 1; // The event was consumed by the callback handler
1720}
1721
1722// Register touch input events
1723static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
1724{
1725 // Register touch points count
1726 CORE.Input.Touch.pointCount = touchEvent->numTouches;
1727
1728 double canvasWidth = 0.0;
1729 double canvasHeight = 0.0;
1730 // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but
1731 // we are looking for actual CSS size: canvas.style.width and canvas.style.height
1732 // EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight);
1733 emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight);
1734
1735 for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
1736 {
1737 // Register touch points id
1738 CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier;
1739
1740 // Register touch points position
1741 CORE.Input.Touch.position[i] = (Vector2){touchEvent->touches[i].targetX, touchEvent->touches[i].targetY};
1742
1743 // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height
1744 CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth);
1745 CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight);
1746
1747 if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1;
1748 else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
1749 }
1750
1751 // Update mouse position if we detect a single touch.
1752 if (CORE.Input.Touch.pointCount == 1)
1753 {
1754 CORE.Input.Mouse.currentPosition.x = CORE.Input.Touch.position[0].x;
1755 CORE.Input.Mouse.currentPosition.y = CORE.Input.Touch.position[0].y;
1756 }
1757
1758#if defined(SUPPORT_GESTURES_SYSTEM)
1759 GestureEvent gestureEvent = {0};
1760
1761 gestureEvent.pointCount = CORE.Input.Touch.pointCount;
1762
1763 // Register touch actions
1764 if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
1765 else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP;
1766 else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
1767 else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
1768
1769 for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
1770 {
1771 gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
1772 gestureEvent.position[i] = CORE.Input.Touch.position[i];
1773
1774 // Normalize gestureEvent.position[i]
1775 gestureEvent.position[i].x /= (float)GetScreenWidth();
1776 gestureEvent.position[i].y /= (float)GetScreenHeight();
1777 }
1778
1779 // Gesture data is sent to gestures system for processing
1780 ProcessGestureEvent(gestureEvent);
1781#endif
1782
1783 if (eventType == EMSCRIPTEN_EVENT_TOUCHEND)
1784 {
1785 CORE.Input.Touch.pointCount--;
1786 if (CORE.Input.Touch.pointCount < 0) CORE.Input.Touch.pointCount = 0;
1787 }
1788
1789 return 1; // The event was consumed by the callback handler
1790}
1791
1792// EOF

Generated with cgit - Back to sebastiano.tronto.net