aboutsummaryrefslogtreecommitdiff
path: root/raylib/src/platforms/rcore_drm.c
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/rcore_drm.c
downloadminesweeper-123144c93bfc77883c8fb517828b47bbe13b8671.tar.gz
minesweeper-123144c93bfc77883c8fb517828b47bbe13b8671.zip
Initial commitHEADmaster
Diffstat (limited to 'raylib/src/platforms/rcore_drm.c')
-rw-r--r--raylib/src/platforms/rcore_drm.c1942
1 files changed, 1942 insertions, 0 deletions
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

Generated with cgit - Back to sebastiano.tronto.net