rcore_drm.c (75745B)
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 93 typedef 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 //---------------------------------------------------------------------------------- 135 extern CoreData CORE; // Global CORE state context 136 137 static 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 149 static 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 158 static 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 //---------------------------------------------------------------------------------- 220 int InitPlatform(void); // Initialize platform (graphics, inputs and more) 221 void ClosePlatform(void); // Close platform 222 223 #if defined(SUPPORT_SSH_KEYBOARD_RPI) 224 static void InitKeyboard(void); // Initialize raw keyboard system 225 static void RestoreKeyboard(void); // Restore keyboard system 226 static void ProcessKeyboard(void); // Process keyboard events 227 #endif 228 229 static void InitEvdevInput(void); // Initialize evdev inputs 230 static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate 231 static void PollKeyboardEvents(void); // Process evdev keyboard events 232 static void PollGamepadEvents(void); // Process evdev gamepad events 233 static void PollMouseEvents(void); // Process evdev mouse events 234 235 static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list 236 static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list 237 static 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 250 bool WindowShouldClose(void) 251 { 252 if (CORE.Window.ready) return CORE.Window.shouldClose; 253 else return true; 254 } 255 256 // Toggle fullscreen mode 257 void ToggleFullscreen(void) 258 { 259 TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); 260 } 261 262 // Toggle borderless windowed mode 263 void ToggleBorderlessWindowed(void) 264 { 265 TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); 266 } 267 268 // Set window state: maximized, if resizable 269 void MaximizeWindow(void) 270 { 271 TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); 272 } 273 274 // Set window state: minimized 275 void MinimizeWindow(void) 276 { 277 TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform"); 278 } 279 280 // Set window state: not minimized/maximized 281 void RestoreWindow(void) 282 { 283 TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); 284 } 285 286 // Set window configuration state using flags 287 void SetWindowState(unsigned int flags) 288 { 289 TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform"); 290 } 291 292 // Clear window configuration state flags 293 void ClearWindowState(unsigned int flags) 294 { 295 TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); 296 } 297 298 // Set icon for window 299 void SetWindowIcon(Image image) 300 { 301 TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); 302 } 303 304 // Set icon for window 305 void SetWindowIcons(Image *images, int count) 306 { 307 TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); 308 } 309 310 // Set title for window 311 void SetWindowTitle(const char *title) 312 { 313 CORE.Window.title = title; 314 } 315 316 // Set window position on screen (windowed mode) 317 void 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 323 void SetWindowMonitor(int monitor) 324 { 325 TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); 326 } 327 328 // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) 329 void 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) 336 void SetWindowMaxSize(int width, int height) 337 { 338 CORE.Window.screenMax.width = width; 339 CORE.Window.screenMax.height = height; 340 } 341 342 // Set window dimensions 343 void 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 349 void SetWindowOpacity(float opacity) 350 { 351 TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); 352 } 353 354 // Set window focused 355 void SetWindowFocused(void) 356 { 357 TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); 358 } 359 360 // Get native window handle 361 void *GetWindowHandle(void) 362 { 363 TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); 364 return NULL; 365 } 366 367 // Get number of monitors 368 int GetMonitorCount(void) 369 { 370 TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); 371 return 1; 372 } 373 374 // Get number of monitors 375 int GetCurrentMonitor(void) 376 { 377 TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); 378 return 0; 379 } 380 381 // Get selected monitor position 382 Vector2 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) 389 int 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) 406 int 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 423 int 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 440 int 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 457 int 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 470 const 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 487 Vector2 GetWindowPosition(void) 488 { 489 return (Vector2){ 0, 0 }; 490 } 491 492 // Get window scale DPI factor for current monitor 493 Vector2 GetWindowScaleDPI(void) 494 { 495 return (Vector2){ 1.0f, 1.0f }; 496 } 497 498 // Set clipboard text content 499 void 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 506 const char *GetClipboardText(void) 507 { 508 TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); 509 return NULL; 510 } 511 512 // Show mouse cursor 513 void ShowCursor(void) 514 { 515 CORE.Input.Mouse.cursorHidden = false; 516 } 517 518 // Hides mouse cursor 519 void HideCursor(void) 520 { 521 CORE.Input.Mouse.cursorHidden = true; 522 } 523 524 // Enables cursor (unlock cursor) 525 void 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) 535 void 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) 545 void 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() 579 double 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 596 void 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 606 int SetGamepadMappings(const char *mappings) 607 { 608 TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); 609 return 0; 610 } 611 612 // Set gamepad vibration 613 void 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 619 void 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 626 void SetMouseCursor(int cursor) 627 { 628 TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); 629 } 630 631 // Get physical key name. 632 const char *GetKeyName(int key) 633 { 634 TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform"); 635 return ""; 636 } 637 638 // Register all input events 639 void 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 710 int 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 1079 void 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) 1169 static 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 1215 static 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 1226 static 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 1330 static 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 1375 static 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 1572 static 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 1632 static 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 1686 static 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 1852 static 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 1874 static 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 1896 static 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