/********************************************************************************************** * * rcore_ template - Functions to manage window, graphics device and inputs * * PLATFORM: * - TODO: Define the target platform for the core * * LIMITATIONS: * - Limitation 01 * - Limitation 02 * * POSSIBLE IMPROVEMENTS: * - Improvement 01 * - Improvement 02 * * ADDITIONAL NOTES: * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: * #define RCORE_PLATFORM_CUSTOM_FLAG * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * - * - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng * * Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, including commercial * applications, and to alter it and redistribute it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not claim that you * wrote the original software. If you use this software in a product, an acknowledgment * in the product documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be misrepresented * as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * **********************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include // must be included before the EGL headers #include #include #include //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- struct finger { TouchAction action; TouchAction gesture_action; int x; int y; }; struct touch { struct finger fingers[1]; int fd; }; // hold all the low level wayland stuff struct wayland_platform { struct wl_compositor *wl_compositor; struct wl_surface *wl_surface; struct wl_egl_window *wl_egl_window; struct wl_region *wl_region; struct wl_shell *wl_shell; struct wl_shell_surface *wl_shell_surface; struct wl_display *wl_display; struct wl_registry *wl_registry; }; // hold all the low level egl stuff struct egl_platform { EGLDisplay display; EGLSurface surface; EGLContext context; EGLConfig config; EGLNativeDisplayType native_display; EGLNativeWindowType native_window; int native_window_width; int native_window_height; }; typedef struct { struct wayland_platform wayland; struct egl_platform egl; struct touch touch; } PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // comma specific code //---------------------------------------------------------------------------------- #define CASE_STR( value ) case value: return #value; const char *eglGetErrorString(EGLint error) { switch(error) { CASE_STR( EGL_SUCCESS ) CASE_STR( EGL_NOT_INITIALIZED ) CASE_STR( EGL_BAD_ACCESS ) CASE_STR( EGL_BAD_ALLOC ) CASE_STR( EGL_BAD_ATTRIBUTE ) CASE_STR( EGL_BAD_CONTEXT ) CASE_STR( EGL_BAD_CONFIG ) CASE_STR( EGL_BAD_CURRENT_SURFACE ) CASE_STR( EGL_BAD_DISPLAY ) CASE_STR( EGL_BAD_SURFACE ) CASE_STR( EGL_BAD_MATCH ) CASE_STR( EGL_BAD_PARAMETER ) CASE_STR( EGL_BAD_NATIVE_PIXMAP ) CASE_STR( EGL_BAD_NATIVE_WINDOW ) CASE_STR( EGL_CONTEXT_LOST ) default: return "Unknown"; } } #undef CASE_STR void wl_shell_surface_handle_ping (void *data, struct wl_shell_surface *shell_surface, uint32_t serial) { wl_shell_surface_pong(shell_surface, serial); } void wl_shell_surface_handle_configure (void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height) { wl_egl_window_resize(platform.egl.native_window, width, height, 0, 0); } void wl_shell_surface_handle_popup_done(void *data, struct wl_shell_surface *shell_surface) { } static struct wl_shell_surface_listener wl_shell_surface_listener = { .ping = &wl_shell_surface_handle_ping, .configure = &wl_shell_surface_handle_configure, .popup_done = &wl_shell_surface_handle_popup_done }; static void wl_registry_handle_global (void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { if (!strcmp(interface, "wl_compositor")) { // Need version 3 of wl_compositor in order to do the rotation transform with wl_surface_set_buffer_transform platform.wayland.wl_compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 3); } else if (!strcmp(interface, "wl_shell")) { platform.wayland.wl_shell = wl_registry_bind(registry, id, &wl_shell_interface, 1); } } static void wl_registry_handle_global_remove (void *data, struct wl_registry *registry, uint32_t id) { } const struct wl_registry_listener wl_registry_listener = { .global = wl_registry_handle_global, .global_remove = wl_registry_handle_global_remove }; static int init_wayland(int width, int height) { platform.wayland.wl_display = wl_display_connect(NULL); if (platform.wayland.wl_display == NULL) { TRACELOG(LOG_WARNING, "COMMA: Failed to create a Wayland display. Failed with: %s", strerror(errno)); return -1; } platform.wayland.wl_compositor = NULL; platform.wayland.wl_shell = NULL; platform.wayland.wl_registry = wl_display_get_registry(platform.wayland.wl_display); wl_registry_add_listener(platform.wayland.wl_registry, &wl_registry_listener, NULL); wl_display_dispatch(platform.wayland.wl_display); wl_display_roundtrip(platform.wayland.wl_display); if (platform.wayland.wl_compositor == NULL || platform.wayland.wl_compositor == NULL) { TRACELOG(LOG_WARNING, "COMMA: Failed to bind Wayland globals"); return -1; } // create a surface with a buffer to do render on it platform.wayland.wl_surface = wl_compositor_create_surface(platform.wayland.wl_compositor); // apply rotation transform to the buffer of the surface wl_surface_set_buffer_transform(platform.wayland.wl_surface, WL_OUTPUT_TRANSFORM_270); platform.wayland.wl_shell_surface = wl_shell_get_shell_surface(platform.wayland.wl_shell, platform.wayland.wl_surface); wl_shell_surface_add_listener(platform.wayland.wl_shell_surface, &wl_shell_surface_listener, NULL); wl_shell_surface_set_toplevel(platform.wayland.wl_shell_surface); platform.wayland.wl_region = wl_compositor_create_region(platform.wayland.wl_compositor); wl_region_add(platform.wayland.wl_region, 0, 0, width, height); wl_surface_set_opaque_region(platform.wayland.wl_surface, platform.wayland.wl_region); // the native window for egl is a our wl_surface platform.egl.native_window = wl_egl_window_create(platform.wayland.wl_surface, width, height); if (platform.egl.native_window == NULL) { TRACELOG(LOG_WARNING, "COMMA: Failed to create a Wayland EGL window"); return -1; } // the native display for egl is a our wl_display platform.egl.native_display = platform.wayland.wl_display; platform.egl.native_window_width = width; platform.egl.native_window_height = height; return 0; } static int init_egl () { EGLint major; EGLint minor; EGLConfig config; EGLint num_config; EGLint frame_buffer_config [] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_NONE }; // ask for an OpenGL ES 2 rendering context EGLint context_config [] = { EGL_CONTEXT_MAJOR_VERSION, 2, EGL_NONE, EGL_NONE }; // get an egl display with our native display (in our case, a wl_display) platform.egl.display = eglGetDisplay(platform.egl.native_display); if (platform.egl.display == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "COMMA: Failed to get an EGL display"); return -1; } if (!eglInitialize(platform.egl.display, &major, &minor)) { TRACELOG(LOG_WARNING, "COMMA: Failed to initialize the EGL display. Error code: %s", eglGetErrorString(eglGetError())); return -1; } TRACELOG(LOG_INFO, "COMMA: Using EGL version %i.%i", major, minor); if (!eglChooseConfig(platform.egl.display, frame_buffer_config, &config, 1, &num_config)) { TRACELOG(LOG_WARNING, "COMMA: Failed to get a valid EGL display config. Error code: %s", eglGetErrorString(eglGetError())); return -1; } TRACELOG(LOG_INFO, "COMMA: Found %i valid EGL display configs", num_config); platform.egl.surface = eglCreateWindowSurface(platform.egl.display, config, platform.egl.native_window, NULL); if (platform.egl.surface == EGL_NO_SURFACE) { TRACELOG(LOG_WARNING, "COMMA: Failed to create an EGL surface. Error code: %s", eglGetErrorString(eglGetError())); return -1; } platform.egl.context = eglCreateContext(platform.egl.display, config, EGL_NO_CONTEXT, context_config); if (platform.egl.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "COMMA: Failed to create an OpenGL ES context. Error code: %s", eglGetErrorString(eglGetError())); return -1; } if (!eglMakeCurrent(platform.egl.display, platform.egl.surface, platform.egl.surface, platform.egl.context)) { TRACELOG(LOG_WARNING, "COMMA: Failed to attach the OpenGL ES context to the EGL surface. Error code: %s", eglGetErrorString(eglGetError())); return -1; } // enable depth testing. Not necessary if only doing 2D glEnable(GL_DEPTH_TEST); return 0; } static int init_touch(const char *dev_path) { platform.touch.fd = open(dev_path, O_RDONLY|O_NONBLOCK); if (platform.touch.fd < 0) { TRACELOG(LOG_WARNING, "COMMA: Failed to open touch device at %s", dev_path); return -1; } platform.touch.fingers[0].x = -1; platform.touch.fingers[0].y = -1; platform.touch.fingers[0].action = TOUCH_ACTION_UP; platform.touch.fingers[0].gesture_action = TOUCH_ACTION_UP; CORE.Input.Touch.currentTouchState[0] = 0; CORE.Input.Mouse.currentPosition.x = -1; CORE.Input.Mouse.currentPosition.y = -1; CORE.Input.Mouse.currentButtonState[0] = 0; CORE.Input.Touch.previousTouchState[0] = 0; CORE.Input.Mouse.previousPosition.x = -1; CORE.Input.Mouse.previousPosition.y = -1; CORE.Input.Mouse.previousButtonState[0] = 0; return 0; } //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- int InitPlatform(void); // Initialize platform (graphics, inputs and more) bool InitGraphicsDevice(void); // Initialize graphics device //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Check if application should close bool WindowShouldClose(void) { return false; } // Toggle fullscreen mode void ToggleFullscreen(void) { TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform"); } // Set window state: not minimized/maximized void RestoreWindow(void) { TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform"); } // Clear window configuration state flags void ClearWindowState(unsigned int flags) { TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); } // Set icon for window void SetWindowIcons(Image *images, int count) { TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window void SetWindowTitle(const char *title) { CORE.Window.title = title; } // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform"); } // Set monitor for the current window void SetWindowMonitor(int monitor) { TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; } // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMaxSize(int width, int height) { CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; } // Set window dimensions void SetWindowSize(int width, int height) { TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform"); } // Set window opacity, value opacity is between 0.0 and 1.0 void SetWindowOpacity(float opacity) { TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); return NULL; } // Get number of monitors int GetMonitorCount(void) { TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); return 1; } // Get number of monitors int GetCurrentMonitor(void) { TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); return 0; } // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); return (Vector2){ 0, 0 }; } // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); return 0; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); return 0; } // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); return 0; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); return 0; } // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); return 0; } // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); return ""; } // Get window position XY on monitor Vector2 GetWindowPosition(void) { TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform"); return (Vector2){ 0, 0 }; } // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); return (Vector2){ 1.0f, 1.0f }; } // Set clipboard text content void SetClipboardText(const char *text) { TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform"); } // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) { TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } // Get clipboard image Image GetClipboardImage(void) { Image image = { 0 }; TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform"); return image; } // Show mouse cursor void ShowCursor(void) { CORE.Input.Mouse.cursorHidden = false; } // Hides mouse cursor void HideCursor(void) { CORE.Input.Mouse.cursorHidden = true; } // Enables cursor (unlock cursor) void EnableCursor(void) { // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); CORE.Input.Mouse.cursorHidden = false; } // Disables cursor (lock cursor) void DisableCursor(void) { // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); CORE.Input.Mouse.cursorHidden = true; } // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { eglSwapBuffers(platform.egl.display, platform.egl.surface); } //---------------------------------------------------------------------------------- // Module Functions Definition: Misc //---------------------------------------------------------------------------------- // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { double time = 0.0; struct timespec ts = { 0 }; clock_gettime(CLOCK_MONOTONIC, &ts); unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() return time; } void OpenURL(const char *url) { TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform"); } //---------------------------------------------------------------------------------- // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } // Set mouse position XY void SetMousePosition(int x, int y) { CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get physical key name. const char *GetKeyName(int key) { TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform"); return ""; } // Register all input events void PollInputEvents(void) { // represents to which finger an event belongs static int slot = 0; // must be called every frames UpdateGestures(); CORE.Input.Touch.previousTouchState[0] = CORE.Input.Touch.currentTouchState[0]; CORE.Input.Mouse.previousButtonState[0] = CORE.Input.Mouse.currentButtonState[0]; CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; CORE.Input.Touch.pointCount = 0; struct input_event event = {0}; while (read(platform.touch.fd, &event, sizeof(struct input_event)) == sizeof(struct input_event)) { if (event.type == SYN_REPORT && slot == 0) { // sync event for main finger CORE.Input.Touch.pointCount = 1; if (platform.touch.fingers[slot].action == TOUCH_ACTION_DOWN) { // detect new touch on screen platform.touch.fingers[slot].gesture_action = platform.touch.fingers[slot].gesture_action == TOUCH_ACTION_UP ? TOUCH_ACTION_DOWN : TOUCH_ACTION_MOVE; CORE.Input.Touch.position[0].x = platform.touch.fingers[0].x; CORE.Input.Touch.position[0].y = platform.touch.fingers[0].y; CORE.Input.Touch.currentTouchState[0] = 1; // map touch state on mouse for conveniance CORE.Input.Mouse.currentPosition.x = platform.touch.fingers[0].x; CORE.Input.Mouse.currentPosition.y = platform.touch.fingers[0].y; CORE.Input.Mouse.currentButtonState[0] = 1; } else if (platform.touch.fingers[slot].action == TOUCH_ACTION_UP) { // finger off the screen platform.touch.fingers[0].gesture_action = TOUCH_ACTION_UP; CORE.Input.Touch.position[0].x = -1; CORE.Input.Touch.position[0].y = -1; CORE.Input.Touch.currentTouchState[0] = 0; CORE.Input.Mouse.currentButtonState[0] = 0; CORE.Input.Touch.previousTouchState[0] = 1; CORE.Input.Mouse.previousButtonState[0] = 1; } // interpret gestures (scroll, pinch, drag, ...) GestureEvent gestureEvent = {0}; gestureEvent.touchAction = platform.touch.fingers[0].gesture_action; gestureEvent.pointCount = CORE.Input.Touch.pointCount; gestureEvent.pointId[0] = 0; gestureEvent.position[0] = CORE.Input.Touch.position[0]; ProcessGestureEvent(gestureEvent); } if (event.type == EV_ABS) { if (event.code == ABS_MT_SLOT) { // new finger on the screen slot = event.value; } if (slot == 0) { // only handle events for main finger if (event.code == ABS_MT_TRACKING_ID) { platform.touch.fingers[slot].action = event.value == -1 ? TOUCH_ACTION_UP : TOUCH_ACTION_DOWN; } else if (event.code == ABS_MT_POSITION_X) { platform.touch.fingers[slot].y = CORE.Window.screen.height - event.value; } else if (event.code == ABS_MT_POSITION_Y) { platform.touch.fingers[slot].x = event.value; } } } } } //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- int InitPlatform(void) { // only support fullscreen CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; // in our case, all those width/height are the same CORE.Window.currentFbo.width = CORE.Window.screen.width; CORE.Window.currentFbo.height = CORE.Window.screen.height; CORE.Window.display.width = CORE.Window.screen.width; CORE.Window.display.height = CORE.Window.screen.height; CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; if (init_wayland(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height)) { TRACELOG(LOG_FATAL, "COMMA: Failed to initialize Wayland"); return -1; } if (init_egl()) { TRACELOG(LOG_FATAL, "COMMA: Failed to initialize EGL"); return -1; } if (init_touch("/dev/input/event2")) { TRACELOG(LOG_FATAL, "COMMA: Failed to initialize touch device"); return -1; } SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); rlLoadExtensions(eglGetProcAddress); InitTimer(); CORE.Storage.basePath = GetWorkingDirectory(); TRACELOG(LOG_INFO, "COMMA: Initialized successfully"); return 0; } void ClosePlatform(void) { }