|
|
|
@@ -9,6 +9,7 @@
|
|
|
|
|
* - PLATFORM_DESKTOP: OSX/macOS
|
|
|
|
|
* - PLATFORM_ANDROID: Android 4.0 (ARM, ARM64)
|
|
|
|
|
* - PLATFORM_RPI: Raspberry Pi 0,1,2,3,4 (Raspbian)
|
|
|
|
|
* - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver
|
|
|
|
|
* - PLATFORM_WEB: HTML5 with asm.js (Chrome, Firefox)
|
|
|
|
|
* - PLATFORM_UWP: Windows 10 App, Windows Phone, Xbox One
|
|
|
|
|
*
|
|
|
|
@@ -55,8 +56,8 @@
|
|
|
|
|
* WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other running processes or
|
|
|
|
|
* blocking the device is not restored properly. Use with care.
|
|
|
|
|
*
|
|
|
|
|
* #define SUPPORT_MOUSE_CURSOR_RPI (Raspberry Pi only)
|
|
|
|
|
* Draw a mouse reference on screen (square cursor box)
|
|
|
|
|
* #define SUPPORT_MOUSE_CURSOR_NATIVE (Raspberry Pi and DRM only)
|
|
|
|
|
* Draw a mouse pointer on screen
|
|
|
|
|
*
|
|
|
|
|
* #define SUPPORT_BUSY_WAIT_LOOP
|
|
|
|
|
* Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
|
|
|
|
@@ -222,7 +223,7 @@
|
|
|
|
|
#include <GLES2/gl2.h> // OpenGL ES 2.0 library
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
#include <fcntl.h> // POSIX file control definitions - open(), creat(), fcntl()
|
|
|
|
|
#include <unistd.h> // POSIX standard function definitions - read(), close(), STDIN_FILENO
|
|
|
|
|
#include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
|
|
|
|
@@ -234,7 +235,15 @@
|
|
|
|
|
#include <linux/input.h> // Linux: Keycodes constants definition (KEY_A, ...)
|
|
|
|
|
#include <linux/joystick.h> // Linux: Joystick support library
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#include "bcm_host.h" // Raspberry Pi VideoCore IV access functions
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
#include <gbm.h> // Generic Buffer Management
|
|
|
|
|
#include <xf86drm.h> // Direct Rendering Manager user-level library interface
|
|
|
|
|
#include <xf86drmMode.h> // Direct Rendering Manager modesetting interface
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "EGL/egl.h" // EGL library - Native platform display device control functions
|
|
|
|
|
#include "EGL/eglext.h" // EGL library - Extensions
|
|
|
|
@@ -266,7 +275,7 @@
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
|
// Defines and Macros
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number
|
|
|
|
|
|
|
|
|
|
#define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...)
|
|
|
|
@@ -306,7 +315,7 @@
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
|
// Types and Structures Definition
|
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
typedef struct {
|
|
|
|
|
pthread_t threadId; // Event reading thread id
|
|
|
|
|
int fd; // File descriptor to the device it is assigned to
|
|
|
|
@@ -337,10 +346,19 @@ typedef struct CoreData {
|
|
|
|
|
GLFWwindow *handle; // Native window handle (graphic device)
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
// NOTE: RPI4 does not support Dispmanx anymore, system should be redesigned
|
|
|
|
|
EGL_DISPMANX_WINDOW_T handle; // Native window handle (graphic device)
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
int fd; // /dev/dri/... file descriptor
|
|
|
|
|
drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector
|
|
|
|
|
int modeIndex; // index of the used mode of connector->modes
|
|
|
|
|
drmModeCrtc *crtc; // crt controller
|
|
|
|
|
struct gbm_device *gbmDevice; // device of Generic Buffer Management (GBM, native platform for EGL on DRM)
|
|
|
|
|
struct gbm_surface *gbmSurface; // surface of GBM
|
|
|
|
|
struct gbm_bo *prevBO; // previous used GBM buffer object (during frame swapping)
|
|
|
|
|
uint32_t prevFB; // previous used GBM framebufer (during frame swapping)
|
|
|
|
|
#endif
|
|
|
|
|
EGLDisplay device; // Native display device (physical screen connection)
|
|
|
|
|
EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
|
|
|
|
|
EGLContext context; // Graphic context, mode in which drawing can be done
|
|
|
|
@@ -384,7 +402,7 @@ typedef struct CoreData {
|
|
|
|
|
} UWP;
|
|
|
|
|
#endif
|
|
|
|
|
struct {
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event<N>"
|
|
|
|
|
#endif
|
|
|
|
|
struct {
|
|
|
|
@@ -394,7 +412,7 @@ typedef struct CoreData {
|
|
|
|
|
|
|
|
|
|
int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input characters queue
|
|
|
|
|
int keyPressedQueueCount; // Input characters queue count
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
int defaultMode; // Default keyboard mode
|
|
|
|
|
struct termios defaultSettings; // Default keyboard settings
|
|
|
|
|
KeyEventFifo lastKeyPressed; // Buffer for holding keydown events as they arrive (Needed due to multitreading of event workers)
|
|
|
|
@@ -412,7 +430,7 @@ typedef struct CoreData {
|
|
|
|
|
char previousButtonState[3]; // Registers previous mouse button state
|
|
|
|
|
int currentWheelMove; // Registers current mouse wheel variation
|
|
|
|
|
int previousWheelMove; // Registers previous mouse wheel variation
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
char currentButtonStateEvdev[3]; // Holds the new mouse state for the next polling event to grab (Can't be written directly due to multithreading, app could miss the update)
|
|
|
|
|
#endif
|
|
|
|
|
} Mouse;
|
|
|
|
@@ -424,13 +442,13 @@ typedef struct CoreData {
|
|
|
|
|
struct {
|
|
|
|
|
int lastButtonPressed; // Register last gamepad button pressed
|
|
|
|
|
int axisCount; // Register number of available gamepad axis
|
|
|
|
|
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
|
|
|
|
|
bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready
|
|
|
|
|
float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
|
|
|
|
|
char currentState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state
|
|
|
|
|
char previousState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
pthread_t threadId; // Gamepad reading thread id
|
|
|
|
|
int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor
|
|
|
|
|
char name[64]; // Gamepad name holder
|
|
|
|
@@ -444,7 +462,7 @@ typedef struct CoreData {
|
|
|
|
|
double draw; // Time measure for frame draw
|
|
|
|
|
double frame; // Time measure for one frame
|
|
|
|
|
double target; // Desired time for one frame, if 0 not applied
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_UWP)
|
|
|
|
|
unsigned long long base; // Base time measure for hi-res timer
|
|
|
|
|
#endif
|
|
|
|
|
} Time;
|
|
|
|
@@ -519,7 +537,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent
|
|
|
|
|
static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
#if defined(SUPPORT_SSH_KEYBOARD_RPI)
|
|
|
|
|
static void InitKeyboard(void); // Init raw keyboard system (standard input reading)
|
|
|
|
|
static void ProcessKeyboard(void); // Process keyboard events
|
|
|
|
@@ -535,7 +553,14 @@ static void *EventThread(void *arg); // Input device events r
|
|
|
|
|
|
|
|
|
|
static void InitGamepad(void); // Init raw gamepad input
|
|
|
|
|
static void *GamepadThread(void *arg); // Mouse reading thread
|
|
|
|
|
#endif // PLATFORM_RPI
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list
|
|
|
|
|
static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list
|
|
|
|
|
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
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#endif // PLATFORM_RPI || PLATFORM_DRM
|
|
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
// NOTE: We include Sleep() function signature here to avoid windows.h inclusion
|
|
|
|
@@ -566,7 +591,7 @@ struct android_app *GetAndroidApp(void)
|
|
|
|
|
return CORE.Android.app;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(PLATFORM_RPI) && !defined(SUPPORT_SSH_KEYBOARD_RPI)
|
|
|
|
|
#if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && !defined(SUPPORT_SSH_KEYBOARD_RPI)
|
|
|
|
|
// Init terminal (block echo and signal short cuts)
|
|
|
|
|
static void InitTerminal(void)
|
|
|
|
|
{
|
|
|
|
@@ -717,7 +742,7 @@ void InitWindow(int width, int height, const char *title)
|
|
|
|
|
SetTextureFilter(GetFontDefault().texture, FILTER_BILINEAR);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
// Init raw input system
|
|
|
|
|
InitEvdevInput(); // Evdev inputs initialization
|
|
|
|
|
InitGamepad(); // Gamepad init
|
|
|
|
@@ -781,12 +806,59 @@ void CloseWindow(void)
|
|
|
|
|
timeEndPeriod(1); // Restore time period
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
if (CORE.Window.prevFB)
|
|
|
|
|
{
|
|
|
|
|
drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
|
|
|
|
|
CORE.Window.prevFB = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CORE.Window.prevBO)
|
|
|
|
|
{
|
|
|
|
|
gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
|
|
|
|
|
CORE.Window.prevBO = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CORE.Window.gbmSurface)
|
|
|
|
|
{
|
|
|
|
|
gbm_surface_destroy(CORE.Window.gbmSurface);
|
|
|
|
|
CORE.Window.gbmSurface = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CORE.Window.gbmDevice)
|
|
|
|
|
{
|
|
|
|
|
gbm_device_destroy(CORE.Window.gbmDevice);
|
|
|
|
|
CORE.Window.gbmDevice = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(CORE.Window.crtc)
|
|
|
|
|
{
|
|
|
|
|
if (CORE.Window.connector)
|
|
|
|
|
{
|
|
|
|
|
drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id,
|
|
|
|
|
CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode);
|
|
|
|
|
drmModeFreeConnector(CORE.Window.connector);
|
|
|
|
|
CORE.Window.connector = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drmModeFreeCrtc(CORE.Window.crtc);
|
|
|
|
|
CORE.Window.crtc = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CORE.Window.fd != -1)
|
|
|
|
|
{
|
|
|
|
|
close(CORE.Window.fd);
|
|
|
|
|
CORE.Window.fd = -1;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Close surface, context and display
|
|
|
|
|
if (CORE.Window.device != EGL_NO_DISPLAY)
|
|
|
|
|
{
|
|
|
|
|
#if !defined(PLATFORM_DRM)
|
|
|
|
|
eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
if (CORE.Window.surface != EGL_NO_SURFACE)
|
|
|
|
|
{
|
|
|
|
|
eglDestroySurface(CORE.Window.device, CORE.Window.surface);
|
|
|
|
@@ -804,7 +876,7 @@ void CloseWindow(void)
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
// Wait for mouse and gamepad threads to finish before closing
|
|
|
|
|
// NOTE: Those threads should already have finished at this point
|
|
|
|
|
// because they are controlled by CORE.Window.shouldClose variable
|
|
|
|
@@ -860,7 +932,7 @@ bool WindowShouldClose(void)
|
|
|
|
|
else return true;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_UWP)
|
|
|
|
|
if (CORE.Window.ready) return CORE.Window.shouldClose;
|
|
|
|
|
else return true;
|
|
|
|
|
#endif
|
|
|
|
@@ -982,7 +1054,7 @@ void ToggleFullscreen(void)
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
TRACELOG(LOG_WARNING, "SYSTEM: Failed to toggle to windowed mode");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
@@ -1251,6 +1323,12 @@ int GetMonitorRefreshRate(int monitor)
|
|
|
|
|
return vidmode->refreshRate;
|
|
|
|
|
}
|
|
|
|
|
else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
|
|
|
|
|
#endif
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0))
|
|
|
|
|
{
|
|
|
|
|
return CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@@ -1400,10 +1478,13 @@ void BeginDrawing(void)
|
|
|
|
|
// End canvas drawing and swap buffers (double buffering)
|
|
|
|
|
void EndDrawing(void)
|
|
|
|
|
{
|
|
|
|
|
#if defined(PLATFORM_RPI) && defined(SUPPORT_MOUSE_CURSOR_RPI)
|
|
|
|
|
// On RPI native mode we have no system mouse cursor, so,
|
|
|
|
|
#if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_MOUSE_CURSOR_NATIVE)
|
|
|
|
|
// On native mode we have no system mouse cursor, so,
|
|
|
|
|
// we draw a small rectangle for user reference
|
|
|
|
|
DrawRectangle(CORE.Input.Mouse.position.x, CORE.Input.Mouse.position.y, 3, 3, MAROON);
|
|
|
|
|
if (!CORE.Input.Mouse.cursorHidden)
|
|
|
|
|
{
|
|
|
|
|
DrawRectangle(CORE.Input.Mouse.position.x, CORE.Input.Mouse.position.y, 3, 3, MAROON);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2)
|
|
|
|
@@ -1806,7 +1887,7 @@ double GetTime(void)
|
|
|
|
|
return glfwGetTime(); // Elapsed time since glfwInit()
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
struct timespec ts;
|
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
|
|
|
unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
|
|
|
|
@@ -2439,7 +2520,7 @@ const char *GetGamepadName(int gamepad)
|
|
|
|
|
#if defined(PLATFORM_DESKTOP)
|
|
|
|
|
if (CORE.Input.Gamepad.ready[gamepad]) return glfwGetJoystickName(gamepad);
|
|
|
|
|
else return NULL;
|
|
|
|
|
#elif defined(PLATFORM_RPI)
|
|
|
|
|
#elif defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name);
|
|
|
|
|
|
|
|
|
|
return CORE.Input.Gamepad.name;
|
|
|
|
@@ -2451,7 +2532,7 @@ const char *GetGamepadName(int gamepad)
|
|
|
|
|
// Return gamepad axis count
|
|
|
|
|
int GetGamepadAxisCount(int gamepad)
|
|
|
|
|
{
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
int axisCount = 0;
|
|
|
|
|
if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount);
|
|
|
|
|
CORE.Input.Gamepad.axisCount = axisCount;
|
|
|
|
@@ -2657,7 +2738,7 @@ int GetTouchX(void)
|
|
|
|
|
{
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
|
|
|
|
|
return (int)CORE.Input.Touch.position[0].x;
|
|
|
|
|
#else // PLATFORM_DESKTOP, PLATFORM_RPI
|
|
|
|
|
#else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM
|
|
|
|
|
return GetMouseX();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
@@ -2667,7 +2748,7 @@ int GetTouchY(void)
|
|
|
|
|
{
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
|
|
|
|
|
return (int)CORE.Input.Touch.position[0].y;
|
|
|
|
|
#else // PLATFORM_DESKTOP, PLATFORM_RPI
|
|
|
|
|
#else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM
|
|
|
|
|
return GetMouseY();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
@@ -2678,7 +2759,7 @@ Vector2 GetTouchPosition(int index)
|
|
|
|
|
{
|
|
|
|
|
Vector2 position = { -1.0f, -1.0f };
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_UWP)
|
|
|
|
|
if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
|
|
|
|
|
else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
|
|
|
|
|
|
|
|
|
@@ -2956,7 +3037,7 @@ static bool InitGraphicsDevice(int width, int height)
|
|
|
|
|
}
|
|
|
|
|
#endif // PLATFORM_DESKTOP || PLATFORM_WEB
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_UWP)
|
|
|
|
|
CORE.Window.fullscreen = true;
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
@@ -2970,6 +3051,157 @@ static bool InitGraphicsDevice(int width, int height)
|
|
|
|
|
VC_RECT_T srcRect;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
CORE.Window.fd = -1;
|
|
|
|
|
CORE.Window.connector = NULL;
|
|
|
|
|
CORE.Window.modeIndex = -1;
|
|
|
|
|
CORE.Window.crtc = NULL;
|
|
|
|
|
CORE.Window.gbmDevice = NULL;
|
|
|
|
|
CORE.Window.gbmSurface = NULL;
|
|
|
|
|
CORE.Window.prevBO = NULL;
|
|
|
|
|
CORE.Window.prevFB = 0;
|
|
|
|
|
|
|
|
|
|
#if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
|
|
|
|
|
CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
|
|
|
|
|
#else
|
|
|
|
|
TRACELOG(LOG_INFO, "DISPLAY: no graphic card set, trying card1");
|
|
|
|
|
CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // VideoCore VI (Raspberry Pi 4)
|
|
|
|
|
if (-1 == CORE.Window.fd)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_INFO, "DISPLAY: failed to open graphic card1, trying card0");
|
|
|
|
|
CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (-1 == CORE.Window.fd)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: failed to open graphic card");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drmModeRes *res = drmModeGetResources(CORE.Window.fd);
|
|
|
|
|
if (!res)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: failed get DRM resources");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TRACELOG(LOG_TRACE, "DISPLAY: %i connectors found", res->count_connectors);
|
|
|
|
|
for (size_t i = 0; i < res->count_connectors; i++)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "DISPLAY: connector index %i", i);
|
|
|
|
|
drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]);
|
|
|
|
|
TRACELOG(LOG_TRACE, "DISPLAY: there are %i connector modes", con->count_modes);
|
|
|
|
|
if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "DRM mode connected");
|
|
|
|
|
CORE.Window.connector = con;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "DRM mode NOT connected (deleting)");
|
|
|
|
|
drmModeFreeConnector(con);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!CORE.Window.connector)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "no suitable DRM connector found");
|
|
|
|
|
drmModeFreeResources(res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id);
|
|
|
|
|
if (!enc)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "failed to get DRM mode encoder");
|
|
|
|
|
drmModeFreeResources(res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id);
|
|
|
|
|
if (!CORE.Window.crtc)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "failed to get DRM mode crtc");
|
|
|
|
|
drmModeFreeEncoder(enc);
|
|
|
|
|
drmModeFreeResources(res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If InitWindow should use the current mode find it in the connector's mode list
|
|
|
|
|
if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "selecting DRM connector mode for current used mode");
|
|
|
|
|
|
|
|
|
|
CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode);
|
|
|
|
|
|
|
|
|
|
if (CORE.Window.modeIndex < 0)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "no matching DRM connector mode found");
|
|
|
|
|
drmModeFreeEncoder(enc);
|
|
|
|
|
drmModeFreeResources(res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CORE.Window.screen.width = CORE.Window.display.width;
|
|
|
|
|
CORE.Window.screen.height = CORE.Window.display.height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT;
|
|
|
|
|
const int fps = (CORE.Time.target > 0) ? (1.0 / CORE.Time.target) : 60;
|
|
|
|
|
// try to find an exact matching mode
|
|
|
|
|
CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
|
|
|
|
|
// if nothing found, try to find a nearly matching mode
|
|
|
|
|
if (CORE.Window.modeIndex < 0)
|
|
|
|
|
CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
|
|
|
|
|
// if nothing found, try to find an exactly matching mode including interlaced
|
|
|
|
|
if (CORE.Window.modeIndex < 0)
|
|
|
|
|
CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
|
|
|
|
|
// if nothing found, try to find a nearly matching mode including interlaced
|
|
|
|
|
if (CORE.Window.modeIndex < 0)
|
|
|
|
|
CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
|
|
|
|
|
// if nothing found, there is no suitable mode
|
|
|
|
|
if (CORE.Window.modeIndex < 0)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "no suitable DRM connector mode found");
|
|
|
|
|
drmModeFreeEncoder(enc);
|
|
|
|
|
drmModeFreeResources(res);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay;
|
|
|
|
|
CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay;
|
|
|
|
|
|
|
|
|
|
TRACELOG(LOG_INFO, "DRM: choosen mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name,
|
|
|
|
|
CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay,
|
|
|
|
|
(CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p',
|
|
|
|
|
CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh);
|
|
|
|
|
|
|
|
|
|
// Use the width and height of the surface for render
|
|
|
|
|
CORE.Window.render.width = CORE.Window.screen.width;
|
|
|
|
|
CORE.Window.render.height = CORE.Window.screen.height;
|
|
|
|
|
|
|
|
|
|
drmModeFreeEncoder(enc);
|
|
|
|
|
enc = NULL;
|
|
|
|
|
|
|
|
|
|
drmModeFreeResources(res);
|
|
|
|
|
res = NULL;
|
|
|
|
|
|
|
|
|
|
CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd);
|
|
|
|
|
if (!CORE.Window.gbmDevice)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "failed to create GBM device");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay,
|
|
|
|
|
CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
|
|
|
|
|
if (!CORE.Window.gbmSurface)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "failed to create GBM surface");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
EGLint samples = 0;
|
|
|
|
|
EGLint sampleBuffer = 0;
|
|
|
|
|
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
|
|
|
|
@@ -2982,11 +3214,15 @@ static bool InitGraphicsDevice(int width, int height)
|
|
|
|
|
const EGLint framebufferAttribs[] =
|
|
|
|
|
{
|
|
|
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Type of context support -> Required on RPI?
|
|
|
|
|
//EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android!
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android!
|
|
|
|
|
#endif
|
|
|
|
|
EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5)
|
|
|
|
|
EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6)
|
|
|
|
|
EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5)
|
|
|
|
|
//EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer)
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer)
|
|
|
|
|
#endif
|
|
|
|
|
//EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
|
|
|
|
|
EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
|
|
|
|
|
//EGL_STENCIL_SIZE, 8, // Stencil buffer size
|
|
|
|
@@ -3170,11 +3406,15 @@ static bool InitGraphicsDevice(int width, int height)
|
|
|
|
|
|
|
|
|
|
#endif // PLATFORM_UWP
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
EGLint numConfigs = 0;
|
|
|
|
|
|
|
|
|
|
// Get an EGL device connection
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice);
|
|
|
|
|
#else
|
|
|
|
|
CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
|
|
|
#endif
|
|
|
|
|
if (CORE.Window.device == EGL_NO_DISPLAY)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
|
|
|
|
@@ -3189,8 +3429,64 @@ static bool InitGraphicsDevice(int width, int height)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
if (!eglGetConfigs(CORE.Window.device, NULL, 0, &numConfigs))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TRACELOG(LOG_TRACE, "DISPLAY: %d EGL configs available", numConfigs);
|
|
|
|
|
|
|
|
|
|
EGLConfig *configs = calloc(numConfigs, sizeof(*configs));
|
|
|
|
|
if (!configs) {
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EGLint matchingNumConfigs = 0;
|
|
|
|
|
if (!eglGetConfigs(CORE.Window.device, configs, numConfigs, &matchingNumConfigs)) {
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL configs: 0x%x", eglGetError());
|
|
|
|
|
free(configs);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TRACELOG(LOG_TRACE, "DISPLAY: %d matching EGL configs available", matchingNumConfigs);
|
|
|
|
|
|
|
|
|
|
if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
|
|
|
|
|
free(configs);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find the EGL config that matches the previously setup GBM format
|
|
|
|
|
int found = 0;
|
|
|
|
|
for (EGLint i = 0; i < matchingNumConfigs; ++i) {
|
|
|
|
|
EGLint id = 0;
|
|
|
|
|
if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id)) {
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError());
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (GBM_FORMAT_ARGB8888 == id) {
|
|
|
|
|
TRACELOG(LOG_TRACE, "DISPLAY: using EGL config %d", i);
|
|
|
|
|
CORE.Window.config = configs[i];
|
|
|
|
|
found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(configs);
|
|
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
// Get an appropriate EGL framebuffer configuration
|
|
|
|
|
eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Set rendering API
|
|
|
|
|
eglBindAPI(EGL_OPENGL_ES_API);
|
|
|
|
@@ -3271,9 +3567,32 @@ static bool InitGraphicsDevice(int width, int height)
|
|
|
|
|
vc_dispmanx_update_submit_sync(dispmanUpdate);
|
|
|
|
|
|
|
|
|
|
CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, &CORE.Window.handle, NULL);
|
|
|
|
|
|
|
|
|
|
const unsigned char *const renderer = glGetString(GL_RENDERER);
|
|
|
|
|
if (renderer) {
|
|
|
|
|
TRACELOG(LOG_INFO, "Renderer is: %s\n", renderer);
|
|
|
|
|
} else {
|
|
|
|
|
TRACELOG(LOG_WARNING, "failed to get renderer\n");
|
|
|
|
|
}
|
|
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
|
#endif // PLATFORM_RPI
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL);
|
|
|
|
|
if (EGL_NO_SURFACE == CORE.Window.surface)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// At this point we need to manage render size vs screen size
|
|
|
|
|
// NOTE: This function use and modify global module variables:
|
|
|
|
|
// -> CORE.Window.screen.width/CORE.Window.screen.height
|
|
|
|
|
// -> CORE.Window.render.width/CORE.Window.render.height
|
|
|
|
|
// -> CORE.Window.screenScale
|
|
|
|
|
SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
|
|
|
|
|
#endif // PLATFORM_DRM
|
|
|
|
|
|
|
|
|
|
// There must be at least one frame displayed before the buffers are swapped
|
|
|
|
|
//eglSwapInterval(CORE.Window.device, 1);
|
|
|
|
|
|
|
|
|
@@ -3290,7 +3609,7 @@ static bool InitGraphicsDevice(int width, int height)
|
|
|
|
|
TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
|
|
|
|
|
TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
|
|
|
|
|
}
|
|
|
|
|
#endif // PLATFORM_ANDROID || PLATFORM_RPI || defined(PLATFORM_UWP)
|
|
|
|
|
#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM || PLATFORM_UWP
|
|
|
|
|
|
|
|
|
|
// Initialize OpenGL context (states and resources)
|
|
|
|
|
// NOTE: CORE.Window.screen.width and CORE.Window.screen.height not used, just stored as globals in rlgl
|
|
|
|
@@ -3426,7 +3745,7 @@ static void InitTimer(void)
|
|
|
|
|
timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
struct timespec now;
|
|
|
|
|
|
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
|
|
|
|
@@ -3556,13 +3875,13 @@ static void PollInputEvents(void)
|
|
|
|
|
// Reset key pressed registered
|
|
|
|
|
CORE.Input.Keyboard.keyPressedQueueCount = 0;
|
|
|
|
|
|
|
|
|
|
#if !defined(PLATFORM_RPI)
|
|
|
|
|
#if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM))
|
|
|
|
|
// Reset last gamepad button/axis registered state
|
|
|
|
|
CORE.Input.Gamepad.lastButtonPressed = -1;
|
|
|
|
|
CORE.Input.Gamepad.axisCount = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
// Register previous keys states
|
|
|
|
|
for (int i = 0; i < 512; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
|
|
|
|
|
|
|
|
|
@@ -3751,7 +4070,7 @@ static void PollInputEvents(void)
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_RPI) && defined(SUPPORT_SSH_KEYBOARD_RPI)
|
|
|
|
|
#if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI)
|
|
|
|
|
// NOTE: Keyboard reading could be done using input_event(s) reading or just read from stdin,
|
|
|
|
|
// we now use both methods inside here. 2nd method is still used for legacy purposes (Allows for input trough SSH console)
|
|
|
|
|
ProcessKeyboard();
|
|
|
|
@@ -3768,9 +4087,58 @@ static void SwapBuffers(void)
|
|
|
|
|
glfwSwapBuffers(CORE.Window.handle);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
|
|
|
|
|
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_UWP)
|
|
|
|
|
eglSwapBuffers(CORE.Window.device, CORE.Window.surface);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_ERROR, "DRM initialization failed, can't swap");
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface);
|
|
|
|
|
if (!bo)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_ERROR, "GBM failed to lock front buffer");
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t fb = 0;
|
|
|
|
|
int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay,
|
|
|
|
|
CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb);
|
|
|
|
|
if (0 != result)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_ERROR, "drmModeAddFB failed with %d", result);
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0,
|
|
|
|
|
&CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]);
|
|
|
|
|
if (0 != result)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_ERROR, "drmModeSetCrtc failed with %d", result);
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CORE.Window.prevFB)
|
|
|
|
|
{
|
|
|
|
|
result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
|
|
|
|
|
if (0 != result)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_ERROR, "drmModeRmFB failed with %d", result);
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
CORE.Window.prevFB = fb;
|
|
|
|
|
|
|
|
|
|
if (CORE.Window.prevBO)
|
|
|
|
|
{
|
|
|
|
|
gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
|
|
|
|
|
}
|
|
|
|
|
CORE.Window.prevBO = bo;
|
|
|
|
|
#endif // defined(PLATFORM_DRM)
|
|
|
|
|
#endif // defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) || defined(PLATFORM_UWP)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
|
|
|
|
@@ -4396,7 +4764,7 @@ static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const void *reserv
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_RPI)
|
|
|
|
|
#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
|
|
|
|
|
|
|
|
|
|
#if defined(SUPPORT_SSH_KEYBOARD_RPI)
|
|
|
|
|
// Initialize Keyboard system (using standard input)
|
|
|
|
@@ -5098,7 +5466,7 @@ static void *GamepadThread(void *arg)
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
#endif // PLATFORM_RPI
|
|
|
|
|
#endif // PLATFORM_RPI || PLATFORM_DRM
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_UWP)
|
|
|
|
|
// UWP function pointers
|
|
|
|
@@ -5373,3 +5741,118 @@ void UWPGestureTouch(int pointer, float x, float y, bool touch)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // PLATFORM_UWP
|
|
|
|
|
|
|
|
|
|
#if defined(PLATFORM_DRM)
|
|
|
|
|
|
|
|
|
|
static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode)
|
|
|
|
|
{
|
|
|
|
|
if (NULL == connector) return -1;
|
|
|
|
|
if (NULL == mode) return -1;
|
|
|
|
|
|
|
|
|
|
// safe bitwise comparison of two modes
|
|
|
|
|
#define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b)) ? sizeof(a) : sizeof(b))
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < connector->count_modes; i++)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "mode %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
|
|
|
|
|
connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
|
|
|
|
|
|
|
|
|
|
if (0 == BINCMP(&CORE.Window.crtc->mode, &CORE.Window.connector->modes[i]))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "above mode selected");
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
#undef BINCMP
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) {
|
|
|
|
|
TRACELOG(LOG_TRACE, "searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no");
|
|
|
|
|
|
|
|
|
|
if (NULL == connector) return -1;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < CORE.Window.connector->count_modes; i++)
|
|
|
|
|
{
|
|
|
|
|
const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
|
|
|
|
|
|
|
|
|
|
TRACELOG(LOG_TRACE, "mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
|
|
|
|
|
|
|
|
|
|
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "but shouldn't choose an interlaced mode");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "mode selected");
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TRACELOG(LOG_TRACE, "no exact matching mode found");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) {
|
|
|
|
|
TRACELOG(LOG_TRACE, "searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no");
|
|
|
|
|
|
|
|
|
|
if (NULL == connector) return -1;
|
|
|
|
|
|
|
|
|
|
int nearestIndex = -1;
|
|
|
|
|
for (int i = 0; i < CORE.Window.connector->count_modes; i++)
|
|
|
|
|
{
|
|
|
|
|
const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
|
|
|
|
|
|
|
|
|
|
TRACELOG(LOG_TRACE, "mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
|
|
|
|
|
(mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
|
|
|
|
|
|
|
|
|
|
if ((mode->hdisplay < width) || (mode->vdisplay < height) | (mode->vrefresh < fps))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "mode is too small");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "shouldn't choose an interlaced mode");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((mode->hdisplay >= width) && (mode->vdisplay >= height) && (mode->vrefresh >= fps))
|
|
|
|
|
{
|
|
|
|
|
const int widthDiff = mode->hdisplay - width;
|
|
|
|
|
const int heightDiff = mode->vdisplay - height;
|
|
|
|
|
const int fpsDiff = mode->vrefresh - fps;
|
|
|
|
|
|
|
|
|
|
if (nearestIndex < 0)
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "first suitable mode");
|
|
|
|
|
nearestIndex = i;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int nearestWidthDiff = CORE.Window.connector->modes[nearestIndex].hdisplay - width;
|
|
|
|
|
const int nearestHeightDiff = CORE.Window.connector->modes[nearestIndex].vdisplay - height;
|
|
|
|
|
const int nearestFpsDiff = CORE.Window.connector->modes[nearestIndex].vrefresh - fps;
|
|
|
|
|
|
|
|
|
|
if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff))
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "mode is nearer than the previous one");
|
|
|
|
|
nearestIndex = i;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TRACELOG(LOG_TRACE, "mode is not nearer");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TRACELOG(LOG_TRACE, "returning nearest mode: %d", nearestIndex);
|
|
|
|
|
return nearestIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|