From fecf56e15aaba160aefe14b241de19262a20ab3e Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 18:36:07 +0200 Subject: [PATCH 0001/1037] WARNING: `rcore` module split per-platform **BIG CHANGE** (#3388) * Submodules (#3311) * Check in current state * Add submodules to Makefile and clean up some imports * Start moving InitGraphicsDeivce * Move android_main and CloseWindow() out of rcore * Move WindowShouldClose out of rcore * Move IsWindowHidden out of rcore * Move IsWindowMinimized out of rcore * Move IsWindowMaximized, IsWindowFocused and IsWindowResized out of rcore * Move ToggleFullscreen out of rcore * Move MaximizeWindow, MinimizeWindow and RestoreWindow out of rcore * Move 13 functions out of rcore: ToggleBorderlessWindowed SetWindowState ClearWindowState SetWindowIcon SetWindowIcons SetWindowTitle SetWindowPosition SetWindowMonitor SetWindowMinSize SetWindowMaxSize SetWindowSize SetWindowOpacity SetWindowFocused * Minor clean up, revert makefile change, include submodules directly in rcore * Fix makefile comment * Remove rcore.h from Makefile * Remove debug include * Move 18 functions from rcore to submodules GetWindowHandle GetMonitorCount GetCurrentMonitor GetMonitorPosition GetMonitorWidth GetMonitorHeight GetMonitorPhysicalHeight GetMonitorRefreshRate GetWindowPosition GetWindowScaleDPI GetMonitorName SetClipboardText GetClipboardText ShowCursor HideCursor EnableCursor DisableCursor GetTime * Move TakeScreenshot, OpenURL, GetGamepadName out of rcore into submodules * remove debugging #defines * Move GetMonitorPhysicalWidth from rcore to submodule * Move GetGamepadAxisCount from rcore * Move SetGamepadMappings out of rcore * Move GetMouseX, GetMouseY, GetMousePosition out of rcore * Move SetMousePosition out of rcore * Move GetMouseWheelMove out of rcore * Move the last functions out of rcore * Move shared function defs and some global var to rcore.h * Clean up rcore.c and rcore.h a little more * Remove unnecessary #define --------- Co-authored-by: MichaelFiber * REVIEWED: `PLATFORM_DESKTOP` Windows building * Revert "REVIEWED: `PLATFORM_DESKTOP` Windows building" This reverts commit 71a12171f768eb25053ef908732b4ce8fdf802f7. * Reviewed Windows building * [split] Fix compilation for web (and desktop) (#3329) * Fix compilation for web * Remove EM_ASM_INT from core_input_gestures_web example * Fix raymath undefined symbols for desktop and web * Remove raylib_opengl_interop from examples Makefile * Revert previous commit (8651c78) * Fix TraceLog for web and desktop * [split] `rcore`, `rcore_web` and `rcore_desktop` changes (batch 2) (#3334) * Fix formatting * Reapply commit 9d230d7 (#3305) that was missing * Reapplies commits 719365f (#3309) and 8a1779b (#3312) that were missing * Reapply commit 5c9cc3f (#3323) that was missing * Reapply commit a2b3b1e that was missing * Revert commit cef25c6 to fix macro redefined warning * Move rcore.h #include to after config.h to fix macro redefinitions warnings * [split] `rcore`, `web`, `desktop`, `android` changes (batch 3) (#3338) * First pass to remove unneeded platform macros for web * Second pass to remove unneeded platform macros for web * Move GetTouchX, GetTouchY, GetTouchPosition from rcore to web, desktop, android * Move SetMouseCursor from rcore to android, desktop, web * [split] `rcore`, `web`, `desktop`, `android` changes (batch 4) (#3343) * Fix ToggleBorderlessWindowed duplicated glfwSetWindowSize calls * First pass to remove unneeded platform macros for android * Second pass to remove unneeded platform macros for android * Remove unneeded platform macros for desktop * Relocate GetGamepadName and update SetGamepadMappings on android, desktop, web * Add missing comment to web * [split] `rcore`, `web`, `desktop`, `android` changes (batch 5) (#3345) * Move SetExitKey from core to android, desktop, web * Move some callbacks from core to desktop and web * Relocate emscripten callbacks on web * Relocate android callbacks on android * Revert "Relocate android callbacks on android" This reverts commit bbdbecc01ea7f871dae56019724386e73611c69c. * Updates UnloadVrStereoConfig on rcore * Update SetClipboardText on android * Fix screenMin/Max default values for android * [split] `rcore`, `drm` changes (#3347) * Tweak makefiles for PLATFORM_DRM and move rcore_drm's dependencies to rcore.h * Move drm functions to rcore_drm.c * Fix a typo in rcore.c * Add SetExitKey to rcore_drm.c --------- Co-authored-by: MichaelFiber * Fix compilation for android (#3360) * Fix android include (#3364) * Reviewed platform split #3313 - Added file headers info - Added TRACELOG message for unimplemented functions - Reviewed code formatting and organization - Several code tweaks * REVIEWED: `GetDirectoryPath()` --------- Co-authored-by: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Co-authored-by: MichaelFiber Co-authored-by: ubkp <118854183+ubkp@users.noreply.github.com> --- .gitignore | 3 + examples/Makefile | 1 + examples/core/core_input_gestures_web.c | 26 +- src/Makefile | 1 + src/rcore.c | 5041 +---------------------- src/rcore.h | 318 ++ src/rcore_android.c | 1267 ++++++ src/rcore_desktop.c | 2074 ++++++++++ src/rcore_drm.c | 2059 +++++++++ src/rcore_web.c | 1604 ++++++++ 10 files changed, 7485 insertions(+), 4909 deletions(-) create mode 100644 src/rcore.h create mode 100644 src/rcore_android.c create mode 100644 src/rcore_desktop.c create mode 100644 src/rcore_drm.c create mode 100644 src/rcore_web.c diff --git a/.gitignore b/.gitignore index ec9325081..fb4865ba3 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,9 @@ xcschememanagement.plist xcuserdata/ DerivedData/ +# VSCode project +.vscode + # Jetbrains project .idea/ cmake-build-*/ diff --git a/examples/Makefile b/examples/Makefile index cd079c312..9404d39cc 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -225,6 +225,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif ifeq ($(PLATFORM),PLATFORM_DRM) + INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) INCLUDE_PATHS += -I/usr/include/libdrm endif diff --git a/examples/core/core_input_gestures_web.c b/examples/core/core_input_gestures_web.c index 0129a983b..e1492244c 100644 --- a/examples/core/core_input_gestures_web.c +++ b/examples/core/core_input_gestures_web.c @@ -135,7 +135,7 @@ void Update(void) } } } - + int fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled if (currentGesture !=0) { @@ -156,16 +156,16 @@ void Update(void) fillLog = 1; } } - + if (fillLog) // If one of the conditions from logMode was met, fill the gesture log { previousGesture = currentGesture; gestureColor = GetGestureColor(currentGesture); if (gestureLogIndex <= 0) gestureLogIndex = GESTURE_LOG_SIZE; gestureLogIndex--; - + // Copy the gesture respective name to the gesture log array - TextCopy(gestureLog[gestureLogIndex], GetGestureName(currentGesture)); + TextCopy(gestureLog[gestureLogIndex], GetGestureName(currentGesture)); } // Handle protractor @@ -182,14 +182,14 @@ void Update(void) { currentAngleDegrees = 0.0f; } - + float currentAngleRadians = ((currentAngleDegrees +90.0f)*PI/180); // Convert the current angle to Radians finalVector = (Vector2){ (angleLength*sinf(currentAngleRadians)) + protractorPosition.x, (angleLength*cosf(currentAngleRadians)) + protractorPosition.y }; // Calculate the final vector for display // Handle touch and mouse pointer points //-------------------------------------------------------------------------------------- #define MAX_TOUCH_COUNT 32 - + Vector2 touchPosition[MAX_TOUCH_COUNT] = { 0 }; Vector2 mousePosition = {0, 0}; if (currentGesture != GESTURE_NONE) @@ -204,7 +204,7 @@ void Update(void) // Draw //-------------------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground(RAYWHITE); // Draw common @@ -235,7 +235,7 @@ void Update(void) // Draw gesture log //-------------------------------------------------------------------------------------- DrawText("Log", gestureLogPosition.x, gestureLogPosition.y, 20, BLACK); - + // Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle) for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE) DrawText(gestureLog[ii], gestureLogPosition.x, gestureLogPosition.y + 410 - i*20, 20, (i == 0 ? gestureColor : LIGHTGRAY)); Color logButton1Color, logButton2Color; @@ -286,7 +286,7 @@ void Update(void) DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f)); DrawCircleV(touchPosition[i], 5.0f, gestureColor); } - + if (touchCount == 2) DrawLineEx(touchPosition[0], touchPosition[1], ((currentGesture == 512)? 8 : 12), gestureColor); } else @@ -308,14 +308,6 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - #if defined( PLATFORM_WEB ) - // Using Emscripten EM_ASM_INT macro, get the page canvas width - const int canvasWidth = EM_ASM_INT( return document.getElementById('canvas').getBoundingClientRect().width; ); - - if (canvasWidth > 400) screenWidth = canvasWidth; - else screenWidth = 400; // Set a minimum width for the screen - #endif - InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures web"); //-------------------------------------------------------------------------------------- diff --git a/src/Makefile b/src/Makefile index 63cbe2a41..e75ae5368 100644 --- a/src/Makefile +++ b/src/Makefile @@ -384,6 +384,7 @@ ifeq ($(PLATFORM),PLATFORM_DRM) # without EGL_NO_X11 eglplatform.h tears Xlib.h in which tears X.h in # which contains a conflicting type Font CFLAGS += -DEGL_NO_X11 + CFLAGS += -Werror=implicit-function-declaration endif # Use Wayland display on Linux desktop ifeq ($(PLATFORM),PLATFORM_DESKTOP) diff --git a/src/rcore.c b/src/rcore.c index 3ba0404d7..44b7ee787 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -53,9 +53,6 @@ * #define SUPPORT_PARTIALBUSY_WAIT_LOOP * Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end * -* #define SUPPORT_EVENTS_WAITING -* Wait for events passively (sleeping while no events) instead of polling them actively every frame -* * #define SUPPORT_SCREEN_CAPTURE * Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() * @@ -105,12 +102,11 @@ #include "config.h" // Defines module configuration flags #endif -#include "utils.h" // Required for: TRACELOG() macros +#include "rcore.h" #define RLGL_IMPLEMENTATION #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 -#define RAYMATH_IMPLEMENTATION // Define external out-of-line implementation #include "raymath.h" // Vector3, Quaternion and Matrix functionality #if defined(SUPPORT_GESTURES_SYSTEM) @@ -141,10 +137,6 @@ #include "external/sdefl.h" // Deflate (RFC 1951) compressor #endif -#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) - #undef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. -#endif #if defined(__linux__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -166,12 +158,6 @@ #endif // OSs #endif // PLATFORM_DESKTOP -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() -#include // Required for: time() [Used in InitTimer()] -#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] - #define _CRT_INTERNAL_NONSTDC_NAMES 1 #include // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()] @@ -199,318 +185,21 @@ #define CHDIR chdir #endif -#if defined(PLATFORM_DESKTOP) - #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 - // NOTE: Already provided by rlgl implementation (on glad.h) - #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management - // NOTE: GLFW3 already includes gl.h (OpenGL) headers - - // Support retrieving native window handlers - #if defined(_WIN32) - typedef void *PVOID; - typedef PVOID HANDLE; - typedef HANDLE HWND; - #define GLFW_EXPOSE_NATIVE_WIN32 - #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h - #include "GLFW/glfw3native.h" - - #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - // NOTE: Those functions require linking with winmm library - unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); - unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); - #endif - #endif - #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) - #include // Required for: timespec, nanosleep(), select() - POSIX - - //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type - //#define GLFW_EXPOSE_NATIVE_WAYLAND - //#define GLFW_EXPOSE_NATIVE_MIR - #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window() - #endif - #if defined(__APPLE__) - #include // Required for: usleep() - - //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition - void *glfwGetCocoaWindow(GLFWwindow* handle); - #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() - #endif - - // TODO: HACK: Added flag if not provided by GLFW when using external library - // Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev - #if !defined(GLFW_MOUSE_PASSTHROUGH) - #define GLFW_MOUSE_PASSTHROUGH 0x0002000D - #endif -#endif - -#if defined(PLATFORM_ANDROID) - //#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) - #include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others - #include // Required for: android_app struct and activity management - #include // Required for: JNIEnv and JavaVM [Used in OpenURL()] - - #include // Native platform windowing system interface - //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) -#endif - -#if defined(PLATFORM_DRM) - #include // POSIX file control definitions - open(), creat(), fcntl() - #include // POSIX standard function definitions - read(), close(), STDIN_FILENO - #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() - #include // POSIX threads management (inputs reading) - #include // POSIX directory browsing - - #include // Required for: ioctl() - UNIX System call for device-specific input/output operations - #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition - #include // Linux: Keycodes constants definition (KEY_A, ...) - #include // Linux: Joystick support library - - #include // Generic Buffer Management (native platform for EGL on DRM) - #include // Direct Rendering Manager user-level library interface - #include // Direct Rendering Manager mode setting (KMS) interface - - #include "EGL/egl.h" // Native platform windowing system interface - #include "EGL/eglext.h" // EGL extensions - //#include "GLES2/gl2.h" // OpenGL ES 2.0 library (not required in this module, only in rlgl) -#endif - -#if defined(PLATFORM_WEB) - #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) - //#define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) - #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management - #include // Required for: timespec, nanosleep(), select() - POSIX - - #include // Emscripten functionality for C - #include // Emscripten HTML5 library -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#if defined(PLATFORM_DRM) - #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number - - #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) - #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events -#endif - -#ifndef MAX_FILEPATH_CAPACITY - #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath -#endif -#ifndef MAX_FILEPATH_LENGTH - #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) -#endif - -#ifndef MAX_KEYBOARD_KEYS - #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported -#endif -#ifndef MAX_MOUSE_BUTTONS - #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported -#endif -#ifndef MAX_GAMEPADS - #define MAX_GAMEPADS 4 // Maximum number of gamepads supported -#endif -#ifndef MAX_GAMEPAD_AXIS - #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) -#endif -#ifndef MAX_GAMEPAD_BUTTONS - #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) -#endif -#ifndef MAX_TOUCH_POINTS - #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported -#endif -#ifndef MAX_KEY_PRESSED_QUEUE - #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue -#endif -#ifndef MAX_CHAR_PRESSED_QUEUE - #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue -#endif - -#ifndef MAX_DECOMPRESSION_SIZE - #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB -#endif - -// Flags operation macros -#define FLAG_SET(n, f) ((n) |= (f)) -#define FLAG_CLEAR(n, f) ((n) &= ~(f)) -#define FLAG_TOGGLE(n, f) ((n) ^= (f)) -#define FLAG_CHECK(n, f) ((n) & (f)) - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -#if defined(PLATFORM_DRM) -typedef struct { - pthread_t threadId; // Event reading thread id - int fd; // File descriptor to the device it is assigned to - int eventNum; // Number of 'event' device - Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) - int touchSlot; // Hold the touch slot number of the currently being sent multitouch block - bool isMouse; // True if device supports relative X Y movements - bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH - bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH - bool isKeyboard; // True if device has letter keycodes - bool isGamepad; // True if device has gamepad buttons -} InputEventWorker; -#endif - -typedef struct { int x; int y; } Point; -typedef struct { unsigned int width; unsigned int height; } Size; - -// Core global state context data -typedef struct CoreData { - struct { -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - GLFWwindow *handle; // GLFW window handle (graphic device) -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) -#if defined(PLATFORM_DRM) - int fd; // File descriptor for /dev/dri/... - drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector - drmModeCrtc *crtc; // CRT Controller - int modeIndex; // Index of the used mode of connector->modes - struct gbm_device *gbmDevice; // GBM device - struct gbm_surface *gbmSurface; // GBM surface - struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) - uint32_t prevFB; // Previous GBM framebufer (during frame swapping) -#endif // PLATFORM_DRM - 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 - EGLConfig config; // Graphic config -#endif - const char *title; // Window text title const pointer - unsigned int flags; // Configuration flags (bit based), keeps window state - bool ready; // Check if window has been initialized successfully - bool fullscreen; // Check if fullscreen mode is enabled - bool shouldClose; // Check if window set for closing - bool resizedLastFrame; // Check if window has been resized last frame - bool eventWaiting; // Wait for events before ending frame - - Point position; // Window position (required on fullscreen toggle) - Point previousPosition; // Window previous position (required on borderless windowed toggle) - Size display; // Display width and height (monitor, device-screen, LCD, ...) - Size screen; // Screen width and height (used render area) - Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) - Size currentFbo; // Current render width and height (depends on active fbo) - Size render; // Framebuffer width and height (render area, including black bars if required) - Point renderOffset; // Offset from render area (must be divided by 2) - Size screenMin; // Screen minimum width and height (for resizable window) - Size screenMax; // Screen maximum width and height (for resizable window) - Matrix screenScale; // Matrix to scale screen (framebuffer rendering) - - char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) - unsigned int dropFileCount; // Count dropped files strings - - } Window; -#if defined(PLATFORM_ANDROID) - struct { - bool appEnabled; // Flag to detect if app is active ** = true - struct android_app *app; // Android activity - struct android_poll_source *source; // Android events polling source - bool contextRebindRequired; // Used to know context rebind required - } Android; -#endif - struct { - const char *basePath; // Base path for data storage - } Storage; - struct { -#if defined(PLATFORM_DRM) - InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" -#endif - struct { - int exitKey; // Default exit key - char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state - char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state - // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially - char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. - - int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue - int keyPressedQueueCount; // Input keys queue count - - int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) - int charPressedQueueCount; // Input characters queue count - -#if defined(PLATFORM_DRM) - int defaultMode; // Default keyboard mode -#if defined(SUPPORT_SSH_KEYBOARD_RPI) - bool evtMode; // Keyboard in event mode -#endif - int defaultFileFlags; // Default IO file flags - struct termios defaultSettings; // Default keyboard settings - int fd; // File descriptor for the evdev keyboard -#endif - } Keyboard; - struct { - Vector2 offset; // Mouse offset - Vector2 scale; // Mouse scaling - Vector2 currentPosition; // Mouse position on screen - Vector2 previousPosition; // Previous mouse position - - int cursor; // Tracks current mouse cursor - bool cursorHidden; // Track if cursor is hidden - bool cursorOnScreen; // Tracks if cursor is inside client area - - char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state - char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state - Vector2 currentWheelMove; // Registers current mouse wheel variation - Vector2 previousWheelMove; // Registers previous mouse wheel variation -#if defined(PLATFORM_DRM) - Vector2 eventWheelMove; // Registers the event mouse wheel variation - // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update - char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab -#endif - } Mouse; - struct { - int pointCount; // Number of touch points active - int pointId[MAX_TOUCH_POINTS]; // Point identifiers - Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen - char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state - char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state - } Touch; - struct { - int lastButtonPressed; // Register last gamepad button pressed - int axisCount; // Register number of available gamepad axis - bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready - char name[MAX_GAMEPADS][64]; // Gamepad name holder - char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state - char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state - float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state -#if defined(PLATFORM_DRM) - pthread_t threadId; // Gamepad reading thread id - int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor -#endif - } Gamepad; - } Input; - struct { - double current; // Current time measure - double previous; // Previous time measure - double update; // Time measure for frame update - 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_DRM) - unsigned long long int base; // Base time measure for hi-res timer -#endif - unsigned int frameCounter; // Frame counter - } Time; -} CoreData; - //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- RLAPI const char *raylib_version = RAYLIB_VERSION; // raylib version exported symbol, required for some bindings -static CoreData CORE = { 0 }; // Global CORE state context +CoreData CORE = { 0 }; // Global CORE state context #if defined(SUPPORT_SCREEN_CAPTURE) -static int screenshotCounter = 0; // Screenshots counter +static int screenshotCounter = 0; // Screenshots counter #endif #if defined(SUPPORT_GIF_RECORDING) -static int gifFrameCounter = 0; // GIF frames counter -static bool gifRecording = false; // GIF recording state -static MsfGifState gifState = { 0 }; // MSGIF context state +int gifFrameCounter = 0; // GIF frames counter +bool gifRecording = false; // GIF recording state +MsfGifState gifState = { 0 }; // MSGIF context state #endif #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -602,79 +291,22 @@ static bool eventsRecording = false; // Record events //----------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- -// Other Modules Functions Declaration (required by core) +// Module Functions Declaration +// NOTE: Those functions are common for all platforms! //---------------------------------------------------------------------------------- + #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) -extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow() -extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory +extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow() +extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- -static void InitTimer(void); // Initialize timer (hi-resolution if available) -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device -static void SetupFramebuffer(int width, int height); // Setup main framebuffer -static void SetupViewport(int width, int height); // Set viewport for a provided width and height +static void InitTimer(void); // Initialize timer (hi-resolution if available) +static void SetupFramebuffer(int width, int height); // Setup main framebuffer +static void SetupViewport(int width, int height); // Set viewport for a provided width and height static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories recursively from a base path -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) -static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error -// Window callbacks events -static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized -#if !defined(PLATFORM_WEB) -static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized -#endif -static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored -static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus -static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window -// Input callbacks events -static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed -static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) -static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed -static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move -static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel -static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area -#endif - -#if defined(PLATFORM_ANDROID) -static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands -static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs -#endif - -#if defined(PLATFORM_WEB) -static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData); -static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData); -static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); - -static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); -static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); -static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); -#endif - -#if defined(PLATFORM_DRM) -static void InitKeyboard(void); // Initialize raw keyboard system -static void RestoreKeyboard(void); // Restore keyboard system -#if defined(SUPPORT_SSH_KEYBOARD_RPI) -static void ProcessKeyboard(void); // Process keyboard events -#endif - -static void InitEvdevInput(void); // Initialize evdev inputs -static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate -static void PollKeyboardEvents(void); // Process evdev keyboard events. -static void *EventThread(void *arg); // Input device events reading thread - -static void InitGamepad(void); // Initialize raw gamepad input -static void *GamepadThread(void *arg); // Mouse reading thread - -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 // PLATFORM_DRM - #if defined(SUPPORT_EVENTS_AUTOMATION) static void LoadAutomationEvents(const char *fileName); // Load automation events from file static void ExportAutomationEvents(const char *fileName); // Export recorded automation events into a file @@ -691,443 +323,73 @@ void __stdcall Sleep(unsigned long msTimeout); // Required for: Wai const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' #endif // !SUPPORT_MODULE_RTEXT +// Include platform-specific submodules +#if defined(PLATFORM_DESKTOP) + #include "rcore_desktop.c" +#elif defined(PLATFORM_WEB) + #include "rcore_web.c" +#elif defined(PLATFORM_DRM) + #include "rcore_drm.c" +#elif defined(PLATFORM_ANDROID) + #include "rcore_android.c" +#else + // Software rendering backend, user needs to provide buffer ;) +#endif + //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions //---------------------------------------------------------------------------------- -#if defined(PLATFORM_ANDROID) -// To allow easier porting to android, we allow the user to define a -// main function which we call from android_main, defined by ourselves -extern int main(int argc, char *argv[]); -void android_main(struct android_app *app) -{ - char arg0[] = "raylib"; // NOTE: argv[] are mutable - CORE.Android.app = app; +// NOTE: Multiple window/display/monitor management functions have been moved to platform-specific modules + +// Platform-specific functions: +//void InitWindow(int width, int height, const char *title); +//void CloseWindow(void); +//bool WindowShouldClose(void) +//bool IsWindowHidden(void) +//bool IsWindowMinimized(void) +//bool IsWindowMaximized(void) +//bool IsWindowFocused(void) +//bool IsWindowResized(void) +//void ToggleFullscreen(void) +//void MaximizeWindow(void) +//void MinimizeWindow(void) +//void RestoreWindow(void) +//void ToggleBorderlessWindowed(void) +//void SetWindowState(unsigned int flags) +//void ClearWindowState(unsigned int flags) +//void SetWindowIcon(Image image) +//void SetWindowIcons(Image *images, int count) +//void SetWindowTitle(const char *title) +//void SetWindowPosition(int x, int y) +//void SetWindowMonitor(int monitor) +//void SetWindowMinSize(int width, int height) +//void SetWindowMaxSize(int width, int height) +//void SetWindowSize(int width, int height) +//void SetWindowOpacity(float opacity) +//void SetWindowFocused(void) +//void *GetWindowHandle(void) +//int GetMonitorCount(void) +//int GetCurrentMonitor(void) +//Vector2 GetMonitorPosition(int monitor) +//int GetMonitorWidth(int monitor) +//int GetMonitorHeight(int monitor) +//int GetMonitorPhysicalWidth(int monitor) +//int GetMonitorPhysicalHeight(int monitor) +//int GetMonitorRefreshRate(int monitor) +//const char *GetMonitorName(int monitor) +//Vector2 GetWindowPosition(void) +//Vector2 GetWindowScaleDPI(void) +//void SetClipboardText(const char *text) +//const char *GetClipboardText(void) +//void ShowCursor(void) +//void HideCursor(void) +//void EnableCursor(void) +//void DisableCursor(void) +//double GetTime(void) +//void TakeScreenshot(const char *fileName) +//void OpenURL(const char *url) - // NOTE: Return from main is ignored - (void)main(1, (char *[]) { arg0, NULL }); - - // Request to end the native activity - ANativeActivity_finish(app->activity); - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Waiting for application events before complete finishing - while (!app->destroyRequested) - { - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) - { - if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); - } - } -} - -// NOTE: Add this to header (if apps really need it) -struct android_app *GetAndroidApp(void) -{ - return CORE.Android.app; -} -#endif - -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN -#if defined(SUPPORT_EVENTS_WAITING) - CORE.Window.eventWaiting = true; -#endif - -#if defined(PLATFORM_ANDROID) - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - - // Set desired windows flags before initializing anything - ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER - - int orientation = AConfiguration_getOrientation(CORE.Android.app->config); - - if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); - else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); - - // TODO: Automatic orientation doesn't seem to work - if (width <= height) - { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); - } - else - { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); - } - - //AConfiguration_getDensity(CORE.Android.app->config); - //AConfiguration_getKeyboard(CORE.Android.app->config); - //AConfiguration_getScreenSize(CORE.Android.app->config); - //AConfiguration_getScreenLong(CORE.Android.app->config); - - // Initialize App command system - // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - CORE.Android.app->onAppCmd = AndroidCommandCallback; - - // Initialize input events system - CORE.Android.app->onInputEvent = AndroidInputCallback; - - // Initialize assets manager - InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath); - - // Initialize base path for storage - CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath; - - TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Wait for window to be initialized (display and context) - while (!CORE.Window.ready) - { - // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) - { - // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); - - // NOTE: Never close window, native activity is controlled by the system! - //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true; - } - } -#endif -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) - // Initialize graphics device (display device and OpenGL context) - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); - - // Initialize random seed - srand((unsigned int)time(NULL)); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); - } - #endif -#else - #if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes - #endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(PLATFORM_DRM) - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) -#endif - -#if defined(PLATFORM_WEB) - // Setup callback functions for the DOM events - emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - - // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review - // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) - //emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Trigger this once to get initial window sizing - EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - - // Support keyboard events -> Not used, GLFW.JS takes care of that - //emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - //emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - - // Support mouse events - emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); - - // Support touch events - emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - - // Support gamepad events (not provided by GLFW3 on emscripten) - emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); - emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - -#endif // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_DRM -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwDestroyWindow(CORE.Window.handle); - glfwTerminate(); -#endif - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - -#if defined(PLATFORM_ANDROID) - // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) - { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (CORE.Window.surface != EGL_NO_SURFACE) - { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; - } - - if (CORE.Window.context != EGL_NO_CONTEXT) - { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; - } - - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; - } -#endif - -#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; - } - - // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) - { - if (CORE.Window.surface != EGL_NO_SURFACE) - { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; - } - - if (CORE.Window.context != EGL_NO_CONTEXT) - { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; - } - - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; - } -#endif - -#if 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 - - CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called - - // Close the evdev keyboard - if (CORE.Input.Keyboard.fd != -1) - { - close(CORE.Input.Keyboard.fd); - CORE.Input.Keyboard.fd = -1; - } - - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].threadId) - { - pthread_join(CORE.Input.eventWorker[i].threadId, NULL); - } - } - - if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - -// Check if KEY_ESCAPE pressed or Close icon pressed -bool WindowShouldClose(void) -{ -#if defined(PLATFORM_WEB) - // Emterpreter-Async required to run sync code - // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code - // By default, this function is never called on a web-ready raylib example because we encapsulate - // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously - // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter! - emscripten_sleep(16); - return false; -#endif - -#if defined(PLATFORM_DESKTOP) - if (CORE.Window.ready) - { - // While window minimized, stop loop execution - while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); - - CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle); - - // Reset close status for next frame - glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE); - - return CORE.Window.shouldClose; - } - else return true; -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - if (CORE.Window.ready) return CORE.Window.shouldClose; - else return true; -#endif -} // Check if window has been initialized successfully bool IsWindowReady(void) @@ -1141,713 +403,12 @@ bool IsWindowFullscreen(void) return CORE.Window.fullscreen; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ -#if defined(PLATFORM_DESKTOP) - return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); -#endif - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); -#endif - return false; -} - -// Check if window has been maximized (only PLATFORM_DESKTOP) -bool IsWindowMaximized(void) -{ -#if defined(PLATFORM_DESKTOP) - return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); -#endif - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); -#endif -#if defined(PLATFORM_ANDROID) - return CORE.Android.appEnabled; -#endif - return true; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return CORE.Window.resizedLastFrame; -#else - return false; -#endif -} - // Check if one specific window flag is enabled bool IsWindowState(unsigned int flag) { return ((CORE.Window.flags & flag) > 0); } -// Toggle fullscreen mode (only PLATFORM_DESKTOP) -void ToggleFullscreen(void) -{ -#if defined(PLATFORM_DESKTOP) - if (!CORE.Window.fullscreen) - { - // Store previous window position (in case we exit fullscreen) - glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); - - int monitorCount = 0; - int monitorIndex = GetCurrentMonitor(); - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - // Use current monitor, so we correctly get the display the window is on - GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL; - - if (monitor == NULL) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); - - CORE.Window.fullscreen = false; - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - - glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - else - { - CORE.Window.fullscreen = true; - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - - glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - } - else - { - CORE.Window.fullscreen = false; - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - - glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - - // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) - // NOTE: V-Sync can be enabled by graphic driver configuration - if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1); -#endif -#if defined(PLATFORM_WEB) -/* - EM_ASM - ( - // This strategy works well while using raylib minimal web shell for emscripten, - // it re-scales the canvas to fullscreen using monitor resolution, for tools this - // is a good strategy but maybe games prefer to keep current canvas resolution and - // display it in fullscreen, adjusting monitor resolution if possible - if (document.fullscreenElement) document.exitFullscreen(); - else Module.requestFullscreen(true, true); //false, true); - ); -*/ - //EM_ASM(Module.requestFullscreen(false, false);); -/* - if (!CORE.Window.fullscreen) - { - // Option 1: Request fullscreen for the canvas element - // This option does not seem to work at all: - // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security, - // the user must click once on the canvas to hide the pointer or transition to full screen - //emscripten_request_fullscreen("#canvas", false); - - // Option 2: Request fullscreen for the canvas element with strategy - // This option does not seem to work at all - // Ref: https://github.com/emscripten-core/emscripten/issues/5124 - // EmscriptenFullscreenStrategy strategy = { - // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, - // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, - // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, - // .canvasResizedCallback = EmscriptenWindowResizedCallback, - // .canvasResizedCallbackUserData = NULL - // }; - //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy); - - // Option 3: Request fullscreen for the canvas element with strategy - // It works as expected but only inside the browser (client area) - EmscriptenFullscreenStrategy strategy = { - .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, - .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, - .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, - .canvasResizedCallback = EmscriptenWindowResizedCallback, - .canvasResizedCallbackUserData = NULL - }; - emscripten_enter_soft_fullscreen("#canvas", &strategy); - - int width, height; - emscripten_get_canvas_element_size("#canvas", &width, &height); - TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height); - - CORE.Window.fullscreen = true; // Toggle fullscreen flag - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - } - else - { - //emscripten_exit_fullscreen(); - //emscripten_exit_soft_fullscreen(); - - int width, height; - emscripten_get_canvas_element_size("#canvas", &width, &height); - TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height); - - CORE.Window.fullscreen = false; // Toggle fullscreen flag - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - } -*/ - - CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - TRACELOG(LOG_WARNING, "SYSTEM: Failed to toggle to windowed mode"); -#endif -} - -// Toggle borderless windowed mode (only PLATFORM_DESKTOP) -void ToggleBorderlessWindowed(void) -{ -#if defined(PLATFORM_DESKTOP) - // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it - bool wasOnFullscreen = false; - if (CORE.Window.fullscreen) - { - CORE.Window.previousPosition = CORE.Window.position; - ToggleFullscreen(); - wasOnFullscreen = true; - } - - const int monitor = GetCurrentMonitor(); - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - if (mode) - { - if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) - { - // Store screen position and size - // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here - if (!wasOnFullscreen) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); - CORE.Window.previousScreen = CORE.Window.screen; - - // Set undecorated and topmost modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_TOPMOST; - - // Get monitor position and size - int monitorPosX = 0; - int monitorPosY = 0; - glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY); - const int monitorWidth = mode->width; - const int monitorHeight = mode->height; - glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); - - // Set screen position and size - glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); - glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); - - // Refocus window - glfwFocusWindow(CORE.Window.handle); - - CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; - } - else - { - // Remove topmost and undecorated modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - - // Return previous screen size and position - // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly - glfwSetWindowSize(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); - glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); - - // Refocus window - glfwFocusWindow(CORE.Window.handle); - - CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; - } - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -} - -// Set window state: maximized, if resizable (only PLATFORM_DESKTOP) -void MaximizeWindow(void) -{ -#if defined(PLATFORM_DESKTOP) - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - glfwMaximizeWindow(CORE.Window.handle); - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; - } -#endif -} - -// Set window state: minimized (only PLATFORM_DESKTOP) -void MinimizeWindow(void) -{ -#if defined(PLATFORM_DESKTOP) - // NOTE: Following function launches callback that sets appropriate flag! - glfwIconifyWindow(CORE.Window.handle); -#endif -} - -// Set window state: not minimized/maximized (only PLATFORM_DESKTOP) -void RestoreWindow(void) -{ -#if defined(PLATFORM_DESKTOP) - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - // Restores the specified window if it was previously iconified (minimized) or maximized - glfwRestoreWindow(CORE.Window.handle); - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; - CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; - } -#endif -} - -// Set window configuration state using flags -void SetWindowState(unsigned int flags) -{ -#if defined(PLATFORM_DESKTOP) - // Check previous state and requested state to apply required changes - // NOTE: In most cases the functions already change the flags internally - - // State change: FLAG_VSYNC_HINT - if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0)) - { - glfwSwapInterval(1); - CORE.Window.flags |= FLAG_VSYNC_HINT; - } - - // State change: FLAG_BORDERLESS_WINDOWED_MODE - // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running - if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) - { - ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_FULLSCREEN_MODE - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE)) - { - ToggleFullscreen(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_RESIZABLE - if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_RESIZABLE; - } - - // State change: FLAG_WINDOW_UNDECORATED - if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - } - - // State change: FLAG_WINDOW_HIDDEN - if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) - { - glfwHideWindow(CORE.Window.handle); - CORE.Window.flags |= FLAG_WINDOW_HIDDEN; - } - - // State change: FLAG_WINDOW_MINIMIZED - if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) - { - //GLFW_ICONIFIED - MinimizeWindow(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_MAXIMIZED - if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) - { - //GLFW_MAXIMIZED - MaximizeWindow(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_UNFOCUSED - if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; - } - - // State change: FLAG_WINDOW_TOPMOST - if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_TOPMOST; - } - - // State change: FLAG_WINDOW_ALWAYS_RUN - if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) - { - CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN; - } - - // The following states can not be changed after window creation - - // State change: FLAG_WINDOW_TRANSPARENT - if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); - } - - // State change: FLAG_WINDOW_HIGHDPI - if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); - } - - // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH - if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH; - } - - // State change: FLAG_MSAA_4X_HINT - if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); - } - - // State change: FLAG_INTERLACED_HINT - if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0)) - { - TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); - } -#endif -} - -// Clear window configuration state flags -void ClearWindowState(unsigned int flags) -{ -#if defined(PLATFORM_DESKTOP) - // Check previous state and requested state to apply required changes - // NOTE: In most cases the functions already change the flags internally - - // State change: FLAG_VSYNC_HINT - if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0)) - { - glfwSwapInterval(0); - CORE.Window.flags &= ~FLAG_VSYNC_HINT; - } - - // State change: FLAG_BORDERLESS_WINDOWED_MODE - // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running - if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) - { - ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_FULLSCREEN_MODE - if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0)) - { - ToggleFullscreen(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_RESIZABLE - if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; - } - - // State change: FLAG_WINDOW_HIDDEN - if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) - { - glfwShowWindow(CORE.Window.handle); - CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; - } - - // State change: FLAG_WINDOW_MINIMIZED - if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) - { - RestoreWindow(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_MAXIMIZED - if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) - { - RestoreWindow(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_UNDECORATED - if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - } - - // State change: FLAG_WINDOW_UNFOCUSED - if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; - } - - // State change: FLAG_WINDOW_TOPMOST - if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - } - - // State change: FLAG_WINDOW_ALWAYS_RUN - if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) - { - CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN; - } - - // The following states can not be changed after window creation - - // State change: FLAG_WINDOW_TRANSPARENT - if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); - } - - // State change: FLAG_WINDOW_HIGHDPI - if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); - } - - // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH - if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH; - } - - // State change: FLAG_MSAA_4X_HINT - if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); - } - - // State change: FLAG_INTERLACED_HINT - if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0)) - { - TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); - } -#endif -} - -// Set icon for window (only PLATFORM_DESKTOP) -// NOTE 1: Image must be in RGBA format, 8bit per channel -// NOTE 2: Image is scaled by the OS for all required sizes -void SetWindowIcon(Image image) -{ -#if defined(PLATFORM_DESKTOP) - if (image.data == NULL) - { - // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); - } - else - { - if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - GLFWimage icon[1] = { 0 }; - - icon[0].width = image.width; - icon[0].height = image.height; - icon[0].pixels = (unsigned char *)image.data; - - // NOTE 1: We only support one image icon - // NOTE 2: The specified image data is copied before this function returns - glfwSetWindowIcon(CORE.Window.handle, 1, icon); - } - else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); - } -#endif -} - -// Set icon for window (multiple images, only PLATFORM_DESKTOP) -// NOTE 1: Images must be in RGBA format, 8bit per channel -// NOTE 2: The multiple images are used depending on provided sizes -// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 -void SetWindowIcons(Image *images, int count) -{ -#if defined(PLATFORM_DESKTOP) - if ((images == NULL) || (count <= 0)) - { - // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); - } - else - { - int valid = 0; - GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); - - for (int i = 0; i < count; i++) - { - if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - icons[valid].width = images[i].width; - icons[valid].height = images[i].height; - icons[valid].pixels = (unsigned char *)images[i].data; - - valid++; - } - else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); - } - // NOTE: Images data is copied internally before this function returns - glfwSetWindowIcon(CORE.Window.handle, valid, icons); - - RL_FREE(icons); - } -#endif -} - -// Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) -void SetWindowTitle(const char *title) -{ - CORE.Window.title = title; -#if defined(PLATFORM_DESKTOP) - glfwSetWindowTitle(CORE.Window.handle, title); -#endif -#if defined(PLATFORM_WEB) - emscripten_set_window_title(title); -#endif -} - -// Set window position on screen (windowed mode) -void SetWindowPosition(int x, int y) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetWindowPos(CORE.Window.handle, x, y); -#endif -} - -// Set monitor for the current window -void SetWindowMonitor(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount = 0; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - if (CORE.Window.fullscreen) - { - TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); - - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); - } - else - { - TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); - - const int screenWidth = CORE.Window.screen.width; - const int screenHeight = CORE.Window.screen.height; - int monitorWorkareaX = 0; - int monitorWorkareaY = 0; - int monitorWorkareaWidth = 0; - int monitorWorkareaHeight = 0; - glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); - - // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it - if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); - else - { - const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); - const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); - glfwSetWindowPos(CORE.Window.handle, x, y); - } - } - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -} - -// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) -void SetWindowMinSize(int width, int height) -{ - CORE.Window.screenMin.width = width; - CORE.Window.screenMin.height = height; -#if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); -#endif -#if defined(PLATFORM_WEB) - // Trigger the resize event once to update the window minimum width and height - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); -#endif -} - -// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) -void SetWindowMaxSize(int width, int height) -{ - CORE.Window.screenMax.width = width; - CORE.Window.screenMax.height = height; -#if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); -#endif -#if defined(PLATFORM_WEB) - // Trigger the resize event once to update the window maximum width and height - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); -#endif -} - -// Set window dimensions -void SetWindowSize(int width, int height) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSetWindowSize(CORE.Window.handle, width, height); -#endif -} - -// Set window opacity, value opacity is between 0.0 and 1.0 -void SetWindowOpacity(float opacity) -{ -#if defined(PLATFORM_DESKTOP) - if (opacity >= 1.0f) opacity = 1.0f; - else if (opacity <= 0.0f) opacity = 0.0f; - glfwSetWindowOpacity(CORE.Window.handle, opacity); -#endif -} - -// Set window focused -void SetWindowFocused(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwFocusWindow(CORE.Window.handle); -#endif -} - // Get current screen width int GetScreenWidth(void) { @@ -1872,335 +433,6 @@ int GetRenderHeight(void) return CORE.Window.render.height; } -// Get native window handle -void *GetWindowHandle(void) -{ -#if defined(PLATFORM_DESKTOP) && defined(_WIN32) - // NOTE: Returned handle is: void *HWND (windows.h) - return glfwGetWin32Window(CORE.Window.handle); -#endif -#if defined(PLATFORM_DESKTOP) && defined(__linux__) - // NOTE: Returned handle is: unsigned long Window (X.h) - // typedef unsigned long XID; - // typedef XID Window; - //unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle); - //return NULL; // TODO: Find a way to return value... cast to void *? - return (void *)CORE.Window.handle; -#endif -#if defined(__APPLE__) - // NOTE: Returned handle is: (objc_object *) - return (void *)glfwGetCocoaWindow(CORE.Window.handle); -#endif - - return NULL; -} - -// Get number of monitors -int GetMonitorCount(void) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - glfwGetMonitors(&monitorCount); - return monitorCount; -#else - return 1; -#endif -} - -// Get number of monitors -int GetCurrentMonitor(void) -{ - int index = 0; - -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - GLFWmonitor *monitor = NULL; - - if (monitorCount > 1) - { - if (IsWindowFullscreen()) - { - // Get the handle of the monitor that the specified window is in full screen on - monitor = glfwGetWindowMonitor(CORE.Window.handle); - - for (int i = 0; i < monitorCount; i++) - { - if (monitors[i] == monitor) - { - index = i; - break; - } - } - } - else - { - int x = 0; - int y = 0; - - glfwGetWindowPos(CORE.Window.handle, &x, &y); - - for (int i = 0; i < monitorCount; i++) - { - int mx = 0; - int my = 0; - - monitor = monitors[i]; - glfwGetMonitorPos(monitor, &mx, &my); - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - if (mode) - { - const int width = mode->width; - const int height = mode->height; - - if ((x >= mx) && - (x < (mx + width)) && - (y >= my) && - (y < (my + height))) - { - index = i; - break; - } - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - } - } -#endif - - return index; -} - -// Get selected monitor position -Vector2 GetMonitorPosition(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - int x, y; - glfwGetMonitorPos(monitors[monitor], &x, &y); - - return (Vector2){ (float)x, (float)y }; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return (Vector2){ 0, 0 }; -} - -// Get selected monitor width (currently used by monitor) -int GetMonitorWidth(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - - if (mode) return mode->width; - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -#if defined(PLATFORM_ANDROID) - if (CORE.Android.app->window != NULL) - { - return ANativeWindow_getWidth(CORE.Android.app->window); - } -#endif - return 0; -} - -// Get selected monitor height (currently used by monitor) -int GetMonitorHeight(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - - if (mode) return mode->height; - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -#if defined(PLATFORM_ANDROID) - if (CORE.Android.app->window != NULL) - { - return ANativeWindow_getHeight(CORE.Android.app->window); - } -#endif - return 0; -} - -// Get selected monitor physical width in millimetres -int GetMonitorPhysicalWidth(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - int physicalWidth; - glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL); - return physicalWidth; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return 0; -} - -// Get selected monitor physical height in millimetres -int GetMonitorPhysicalHeight(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - int physicalHeight; - glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight); - return physicalHeight; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return 0; -} - -// Get selected monitor refresh rate -int GetMonitorRefreshRate(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[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; -} - -// Get window position XY on monitor -Vector2 GetWindowPosition(void) -{ - int x = 0; - int y = 0; -#if defined(PLATFORM_DESKTOP) - glfwGetWindowPos(CORE.Window.handle, &x, &y); -#endif - return (Vector2){ (float)x, (float)y }; -} - -// Get window scale DPI factor for current monitor -Vector2 GetWindowScaleDPI(void) -{ - Vector2 scale = { 1.0f, 1.0f }; - -#if defined(PLATFORM_DESKTOP) - float xdpi = 1.0; - float ydpi = 1.0; - Vector2 windowPos = GetWindowPosition(); - - int monitorCount = 0; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - // Check window monitor - for (int i = 0; i < monitorCount; i++) - { - glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi); - - int xpos, ypos, width, height; - glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height); - - if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) && - (windowPos.y >= ypos) && (windowPos.y < ypos + height)) - { - scale.x = xdpi; - scale.y = ydpi; - break; - } - } -#endif - - return scale; -} - -// Get the human-readable, UTF-8 encoded name of the selected monitor -const char *GetMonitorName(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - return glfwGetMonitorName(monitors[monitor]); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return ""; -} - -// Set clipboard text content -void SetClipboardText(const char *text) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetClipboardString(CORE.Window.handle, text); -#endif -#if defined(PLATFORM_WEB) - // Security check to (partially) avoid malicious code - if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character"); - else EM_ASM( { navigator.clipboard.writeText(UTF8ToString($0)); }, text); -#endif -} - -// Get clipboard text content -// NOTE: returned string is allocated and freed by GLFW -const char *GetClipboardText(void) -{ -#if defined(PLATFORM_DESKTOP) - return glfwGetClipboardString(CORE.Window.handle); -#endif -#if defined(PLATFORM_WEB) -/* - // Accessing clipboard data from browser is tricky due to security reasons - // The method to use is navigator.clipboard.readText() but this is an asynchronous method - // that will return at some moment after the function is called with the required data - emscripten_run_script_string("navigator.clipboard.readText() \ - .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \ - .catch(err => { console.error('Failed to read clipboard contents: ', err); });" - ); - - // The main issue is getting that data, one approach could be using ASYNCIFY and wait - // for the data but it requires adding Asyncify emscripten library on compilation - - // Another approach could be just copy the data in a HTML text field and try to retrieve it - // later on if available... and clean it for future accesses -*/ - return NULL; -#endif - return NULL; -} - // Enable waiting for events on EndDrawing(), no automatic event polling void EnableEventWaiting(void) { @@ -2213,62 +445,12 @@ void DisableEventWaiting(void) CORE.Window.eventWaiting = false; } -// Show mouse cursor -void ShowCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); -#endif - - CORE.Input.Mouse.cursorHidden = false; -} - -// Hides mouse cursor -void HideCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); -#endif - - CORE.Input.Mouse.cursorHidden = true; -} - // Check if cursor is not visible bool IsCursorHidden(void) { return CORE.Input.Mouse.cursorHidden; } -// Enables cursor (unlock cursor) -void EnableCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); -#endif -#if defined(PLATFORM_WEB) - emscripten_exit_pointerlock(); -#endif - // 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) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); -#endif -#if defined(PLATFORM_WEB) - emscripten_request_pointerlock("#canvas", 1); -#endif - // Set cursor position in the middle - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); - - CORE.Input.Mouse.cursorHidden = true; -} - // Check if cursor is on the current screen. bool IsCursorOnScreen(void) { @@ -2674,7 +856,7 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) // Unload VR stereo config properties void UnloadVrStereoConfig(VrStereoConfig config) { - //... + TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c"); } // Load shader from files and bind default locations @@ -2952,7 +1134,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - double aspect = ((double)width/(double)height); + float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; double top = camera.fovy/2.0; double right = top*aspect; @@ -2963,6 +1145,8 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh // Calculate view matrix from camera look at (and transpose it) Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); + // TODO: Why not use Vector3Transform(Vector3 v, Matrix mat)? + // Convert world position vector to quaternion Quaternion worldPos = { position.x, position.y, position.z, 1.0f }; @@ -3047,26 +1231,6 @@ float GetFrameTime(void) return (float)CORE.Time.frame; } -// Get elapsed time measure in seconds since InitTimer() -// NOTE: On PLATFORM_DESKTOP InitTimer() is called on InitWindow() -// NOTE: On PLATFORM_DESKTOP, timer is initialized on glfwInit() -double GetTime(void) -{ - double time = 0.0; -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - time = glfwGetTime(); // Elapsed time since glfwInit() -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - 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() -#endif - return time; -} - // Setup window configuration flags (view FLAGS) // NOTE: This function is expected to be called before window creation, // because it sets up some flags for the window creation process. @@ -3078,63 +1242,9 @@ void SetConfigFlags(unsigned int flags) CORE.Window.flags |= flags; } -// NOTE TRACELOG() function is located in [utils.h] - -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_WEB - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - -#if defined(PLATFORM_WEB) - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); -#endif - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - -// Get a random value between min and max (both included) -// WARNING: Ranges higher than RAND_MAX will return invalid results -// More specifically, if (max - min) > INT_MAX there will be an overflow, -// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold -int GetRandomValue(int min, int max) -{ - if (min > max) - { - int tmp = max; - max = min; - min = tmp; - } - - if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) - { - TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); - } - - return (rand()%(abs(max - min) + 1) + min); -} - -// Set the seed for the random number generator -void SetRandomSeed(unsigned int seed) -{ - srand(seed); -} +//---------------------------------------------------------------------------------- +// Module Functions Definition: FileSystem +//---------------------------------------------------------------------------------- // Check if the file exists bool FileExists(const char *fileName) @@ -3290,7 +1400,7 @@ const char *GetFileNameWithoutExt(const char *filePath) // Get directory for a given filePath const char *GetDirectoryPath(const char *filePath) { -/* + /* // NOTE: Directory separator is different in Windows and other platforms, // fortunately, Windows also support the '/' separator, that's the one should be used #if defined(_WIN32) @@ -3298,7 +1408,7 @@ const char *GetDirectoryPath(const char *filePath) #else char separator = '/'; #endif -*/ + */ const char *lastSlash = NULL; static char dirPath[MAX_FILEPATH_LENGTH] = { 0 }; memset(dirPath, 0, MAX_FILEPATH_LENGTH); @@ -3325,8 +1435,10 @@ const char *GetDirectoryPath(const char *filePath) else { // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' - memcpy(dirPath + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0), filePath, strlen(filePath) - (strlen(lastSlash) - 1)); - dirPath[strlen(filePath) - strlen(lastSlash) + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0)] = '\0'; // Add '\0' manually + unsigned char *dirPathPtr = dirPath; + if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2; // Skip drive letter, "C:" + memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1)); + dirPath[strlen(filePath) - strlen(lastSlash) + ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0] = '\0'; // Add '\0' manually } } @@ -3578,6 +1690,37 @@ long GetFileModTime(const char *fileName) return 0; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// Get a random value between min and max (both included) +// WARNING: Ranges higher than RAND_MAX will return invalid results +// More specifically, if (max - min) > INT_MAX there will be an overflow, +// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold +int GetRandomValue(int min, int max) +{ + if (min > max) + { + int tmp = max; + max = min; + min = tmp; + } + + if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) + { + TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); + } + + return (rand()%(abs(max - min) + 1) + min); +} + +// Set the seed for the random number generator +void SetRandomSeed(unsigned int seed) +{ + srand(seed); +} + // Compress data (DEFLATE algorithm) unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize) { @@ -3588,12 +1731,12 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa #if defined(SUPPORT_COMPRESSION_API) // Compress data and generate a valid DEFLATE stream struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB - int bounds = dataSize*2;//sdefl_bound(dataSize); + int bounds = sdefl_bound(dataSize); compData = (unsigned char *)RL_CALLOC(bounds, 1); - + *compDataSize = sdeflate(sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbiw RL_FREE(sdefl); - + TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize); #endif @@ -3719,67 +1862,31 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) return decodedData; } -// Open URL with default system browser (if available) -// NOTE: This function is only safe to use if you control the URL given. -// A user could craft a malicious string performing another action. -// Only call this function yourself not with user input or make sure to check the string yourself. -// Ref: https://github.com/raysan5/raylib/issues/686 -void OpenURL(const char *url) -{ - // Security check to (partially) avoid malicious code on PLATFORM_WEB - if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); - else - { -#if defined(PLATFORM_DESKTOP) - char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char)); - #if defined(_WIN32) - sprintf(cmd, "explorer \"%s\"", url); - #endif - #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) - sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser - #endif - #if defined(__APPLE__) - sprintf(cmd, "open '%s'", url); - #endif - int result = system(cmd); - if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created"); - RL_FREE(cmd); -#endif -#if defined(PLATFORM_WEB) - emscripten_run_script(TextFormat("window.open('%s', '_blank')", url)); -#endif -#if defined(PLATFORM_ANDROID) - JNIEnv *env = NULL; - JavaVM *vm = CORE.Android.app->activity->vm; - (*vm)->AttachCurrentThread(vm, &env, NULL); - - jstring urlString = (*env)->NewStringUTF(env, url); - jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); - jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); - jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); - - jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); - jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); - jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); - jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); - jobject intent = (*env)->AllocObject(env, intentClass); - - (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); - jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); - jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); - (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent); - - (*vm)->DetachCurrentThread(vm); -#endif - } -} - //---------------------------------------------------------------------------------- -// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions +// Module Functions Definition: Inputs //---------------------------------------------------------------------------------- + +// Platform-specific functions +//void SetExitKey(int key) +//const char *GetGamepadName(int gamepad) +//int GetGamepadAxisCount(int gamepad) +//int SetGamepadMappings(const char *mappings) +//int GetMouseX(void) +//int GetMouseY(void) +//Vector2 GetMousePosition(void) +//void SetMousePosition(int x, int y) +//float GetMouseWheelMove(void) +//void SetMouseCursor(int cursor) +//int GetTouchX(void) +//int GetTouchY(void) +//Vector2 GetTouchPosition(int index) +//void SwapScreenBuffer(void) +//void PollInputEvents(void) + // Check if a key has been pressed once bool IsKeyPressed(int key) { + bool pressed = false; if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) @@ -3886,17 +1993,6 @@ int GetCharPressed(void) return value; } -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ -#if !defined(PLATFORM_ANDROID) - CORE.Input.Keyboard.exitKey = key; -#endif -} - -// NOTE: Gamepad support not implemented in emscripten GLFW3 (PLATFORM_WEB) - // Check if a gamepad is available bool IsGamepadAvailable(int gamepad) { @@ -3907,40 +2003,6 @@ bool IsGamepadAvailable(int gamepad) return result; } -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - const char *name = NULL; - -#if defined(PLATFORM_DESKTOP) - if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); -#endif -#if defined(PLATFORM_DRM) - if (CORE.Input.Gamepad.ready[gamepad]) - { - ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); - name = CORE.Input.Gamepad.name[gamepad]; - } -#endif -#if defined(PLATFORM_WEB) - name = CORE.Input.Gamepad.name[gamepad]; -#endif - - return name; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ -#if 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; -#endif - - return CORE.Input.Gamepad.axisCount; -} - // Get axis movement vector for a gamepad float GetGamepadAxisMovement(int gamepad, int axis) { @@ -4002,18 +2064,6 @@ int GetGamepadButtonPressed(void) return CORE.Input.Gamepad.lastButtonPressed; } -// Set internal gamepad mappings -int SetGamepadMappings(const char *mappings) -{ - int result = 0; - -#if defined(PLATFORM_DESKTOP) - result = glfwUpdateGamepadMappings(mappings); -#endif - - return result; -} - // Check if a mouse button has been pressed once bool IsMouseButtonPressed(int button) { @@ -4066,44 +2116,6 @@ bool IsMouseButtonUp(int button) return up; } -// Get mouse position X -int GetMouseX(void) -{ -#if defined(PLATFORM_ANDROID) - return (int)CORE.Input.Touch.position[0].x; -#else - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -#endif -} - -// Get mouse position Y -int GetMouseY(void) -{ -#if defined(PLATFORM_ANDROID) - return (int)CORE.Input.Touch.position[0].y; -#else - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -#endif -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - // TODO: Review touch position on PLATFORM_WEB - -#if defined(PLATFORM_ANDROID) //|| defined(PLATFORM_WEB) - position = GetTouchPosition(0); -#else - // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; -#endif - - return position; -} - // Get mouse delta between frames Vector2 GetMouseDelta(void) { @@ -4115,18 +2127,6 @@ Vector2 GetMouseDelta(void) return delta; } -// 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; - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - // NOTE: emscripten not implemented - glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); -#endif -} - // Set mouse offset // NOTE: Useful when rendering to different size targets void SetMouseOffset(int offsetX, int offsetY) @@ -4141,19 +2141,6 @@ void SetMouseScale(float scaleX, float scaleY) CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY }; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - float result = 0.0f; - -#if !defined(PLATFORM_ANDROID) - if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; - else result = (float)CORE.Input.Mouse.currentWheelMove.y; -#endif - - return result; -} - // Get mouse wheel movement X/Y as a vector Vector2 GetMouseWheelMoveV(void) { @@ -4164,61 +2151,6 @@ Vector2 GetMouseWheelMoveV(void) return result; } -// Set mouse cursor -// NOTE: This is a no-op on platforms other than PLATFORM_DESKTOP -void SetMouseCursor(int cursor) -{ -#if defined(PLATFORM_DESKTOP) - CORE.Input.Mouse.cursor = cursor; - if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL); - else - { - // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values - glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor)); - } -#endif -} - -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) - return (int)CORE.Input.Touch.position[0].x; -#else // PLATFORM_DESKTOP, PLATFORM_DRM - return GetMouseX(); -#endif -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) - return (int)CORE.Input.Touch.position[0].y; -#else // PLATFORM_DESKTOP, PLATFORM_DRM - return GetMouseY(); -#endif -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - -#if defined(PLATFORM_DESKTOP) - // TODO: GLFW does not support multi-touch input just yet - // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch - // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages - if (index == 0) position = GetMousePosition(); -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) - 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); -#endif - - return position; -} - // Get touch point identifier for given index int GetTouchPointId(int index) { @@ -4236,740 +2168,14 @@ int GetTouchPointCount(void) } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) -{ - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - - glfwInitAllocator(&allocator); -*/ -#if defined(__APPLE__) - glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); -#endif - - if (!glfwInit()) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); - return false; - } - - glfwDefaultWindowHints(); // Set default windows hints - //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits - //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits - //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits - //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits - //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits - //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window - //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API - //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers - - // Check window creation flags - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true; - - if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window - else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden - - if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window - else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window - - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window - else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable - - // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; - - // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization - if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; - - if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); - else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); - - if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); - else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); - - // NOTE: Some GLFW flags are not supported on HTML5 -#if defined(PLATFORM_DESKTOP) - if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer - else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer - - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Resize window content area based on the monitor content scale. - // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11. - // On platforms like macOS the resolution of the framebuffer is changed independently of the window size. - glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on - #if defined(__APPLE__) - glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); - #endif - } - else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); - - // Mouse passthrough - if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); - else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); -#endif - - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); - glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 - } - - // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version - // with backward compatibility to older OpenGL versions. - // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context. - - // Check selection OpenGL version - if (rlGetVersion() == RL_OPENGL_21) - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) - } - else if (rlGetVersion() == RL_OPENGL_33) - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! - // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE -#if defined(__APPLE__) - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility -#else - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! -#endif - //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context - } - else if (rlGetVersion() == RL_OPENGL_43) - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint) - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context -#endif - } - else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); -#if defined(PLATFORM_DESKTOP) - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); -#else - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); -#endif - } - else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); -#if defined(PLATFORM_DESKTOP) - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); -#else - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); -#endif - } - -#if defined(PLATFORM_DESKTOP) - // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions. - // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn. - // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience. - // REF: https://github.com/raysan5/raylib/issues/1554 - if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); -#endif - -#if defined(PLATFORM_DESKTOP) - // Find monitor resolution - GLFWmonitor *monitor = glfwGetPrimaryMonitor(); - if (!monitor) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); - return false; - } - - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - - CORE.Window.display.width = mode->width; - CORE.Window.display.height = mode->height; - - // Set screen width/height to the display width/height if they are 0 - if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; - if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; -#endif // PLATFORM_DESKTOP - -#if defined(PLATFORM_WEB) - // NOTE: Getting video modes is not implemented in emscripten GLFW3 version - CORE.Window.display.width = CORE.Window.screen.width; - CORE.Window.display.height = CORE.Window.screen.height; -#endif // PLATFORM_WEB - - if (CORE.Window.fullscreen) - { - // remember center for switchinging from fullscreen to window - if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) - { - // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. - // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11. - CORE.Window.position.x = CORE.Window.display.width/4; - CORE.Window.position.y = CORE.Window.display.height/4; - } - else - { - CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2; - CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2; - } - - if (CORE.Window.position.x < 0) CORE.Window.position.x = 0; - if (CORE.Window.position.y < 0) CORE.Window.position.y = 0; - - // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor - int count = 0; - const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); - - // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height - for (int i = 0; i < count; i++) - { - if ((unsigned int)modes[i].width >= CORE.Window.screen.width) - { - if ((unsigned int)modes[i].height >= CORE.Window.screen.height) - { - CORE.Window.display.width = modes[i].width; - CORE.Window.display.height = modes[i].height; - break; - } - } - } - TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - - // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, - // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), - // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched - // by the sides to fit all monitor space... - - // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight - // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset) - // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale - // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed... - // HighDPI monitors are properly considered in a following similar function: SetupViewport() - SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - - CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); - - // NOTE: Full-screen change, not working properly... - //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - else - { -#if defined(PLATFORM_DESKTOP) - // If we are windowed fullscreen, ensures that window does not minimize when focus is lost - if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) - { - glfwWindowHint(GLFW_AUTO_ICONIFY, 0); - } -#endif - // No-fullscreen window creation - CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); - - if (CORE.Window.handle) - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - } - } - - if (!CORE.Window.handle) - { - glfwTerminate(); - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return false; - } - -// glfwCreateWindow title doesn't work with emscripten. -#if defined(PLATFORM_WEB) - emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); -#endif - - // Set window callback events - glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! -#if !defined(PLATFORM_WEB) - glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback); -#endif - glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); - glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); - - // Set input callback events - glfwSetKeyCallback(CORE.Window.handle, KeyCallback); - glfwSetCharCallback(CORE.Window.handle, CharCallback); - glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); - glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); - - glfwMakeContextCurrent(CORE.Window.handle); - -#if !defined(PLATFORM_WEB) - glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) - - glfwSwapInterval(0); // No V-Sync by default -#endif - - // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) - // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need - // to be activated on web platforms since VSync is enforced there. -#if !defined(PLATFORM_WEB) - if (CORE.Window.flags & FLAG_VSYNC_HINT) - { - // WARNING: It seems to hit a critical render path in Intel HD Graphics - glfwSwapInterval(1); - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); - } -#endif - - int fbWidth = CORE.Window.screen.width; - int fbHeight = CORE.Window.screen.height; - -#if defined(PLATFORM_DESKTOP) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. - // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); - #if !defined(__APPLE__) - glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight); - - // Screen scaling matrix is required in case desired screen area is different from display area - CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); - - // Mouse input scaling for the new screen size - SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); - #endif - } -#endif - - CORE.Window.render.width = fbWidth; - CORE.Window.render.height = fbHeight; - CORE.Window.currentFbo.width = fbWidth; - CORE.Window.currentFbo.height = fbHeight; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - -#endif // PLATFORM_DESKTOP || PLATFORM_WEB - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - CORE.Window.fullscreen = true; - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - -#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 platform-gpu-card"); - CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) - - if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL)) - { - TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); - CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded - } - - if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL)) - { - 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: Connectors found: %i", 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: Connector modes detected: %i", con->count_modes); - if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); - CORE.Window.connector = con; - break; - } - else - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); - drmModeFreeConnector(con); - } - } - - if (!CORE.Window.connector) - { - TRACELOG(LOG_WARNING, "DISPLAY: 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, "DISPLAY: 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, "DISPLAY: 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, "DISPLAY: 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, "DISPLAY: 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, "DISPLAY: Failed to find a suitable DRM connector mode"); - 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, "DISPLAY: Selected DRM connector 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, "DISPLAY: 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, "DISPLAY: Failed to create GBM surface"); - return false; - } -#endif - - EGLint samples = 0; - EGLint sampleBuffer = 0; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - samples = 4; - sampleBuffer = 1; - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); - } - - const EGLint framebufferAttribs[] = - { - EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support -#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) -#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 - EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA - EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) - EGL_NONE - }; - - const EGLint contextAttribs[] = - { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - -#if defined(PLATFORM_ANDROID) || 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"); - return false; - } - - // Initialize the EGL device connection - if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) - { - // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. - TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; - } - -#if defined(PLATFORM_DRM) - if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs)) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); - return false; - } - - TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs); - - EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs)); - if (!configs) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs"); - return false; - } - - EGLint matchingNumConfigs = 0; - 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; - } - - TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs); - - // 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; - } - } - - RL_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); - - // Create an EGL rendering context - CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); - if (CORE.Window.context == EGL_NO_CONTEXT) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; - } -#endif - - // Create an EGL window surface - //--------------------------------------------------------------------------------- -#if defined(PLATFORM_ANDROID) - EGLint displayFormat = 0; - - // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() - // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - - // 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); - - ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); - //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size - - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); -#endif // PLATFORM_ANDROID - -#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); - - if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; - } - else - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - CORE.Window.currentFbo.width = CORE.Window.render.width; - CORE.Window.currentFbo.height = CORE.Window.render.height; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - } -#endif // PLATFORM_ANDROID || PLATFORM_DRM - - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - rlLoadExtensions(glfwGetProcAddress); -#else - rlLoadExtensions(eglGetProcAddress); -#endif - - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(PLATFORM_ANDROID) - CORE.Window.ready = true; -#endif - - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - - return true; -} +// Platform-specific functions +//static bool InitGraphicsDevice(int width, int height) // Set viewport for a provided width and height -static void SetupViewport(int width, int height) +void SetupViewport(int width, int height) { CORE.Window.render.width = width; CORE.Window.render.height = height; @@ -4998,7 +2204,7 @@ static void SetupViewport(int width, int height) // Compute framebuffer size relative to screen size and display size // NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified -static void SetupFramebuffer(int width, int height) +void SetupFramebuffer(int width, int height) { // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var) if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height)) @@ -5075,7 +2281,7 @@ static void SetupFramebuffer(int width, int height) } // Initialize hi-resolution timer -static void InitTimer(void) +void InitTimer(void) { // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. @@ -5142,323 +2348,6 @@ void WaitTime(double seconds) #endif } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSwapBuffers(CORE.Window.handle); -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - eglSwapBuffers(CORE.Window.device, CORE.Window.surface); - -#if defined(PLATFORM_DRM) - - if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); - - struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface); - if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); - - 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 (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); - - 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 (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); - - if (CORE.Window.prevFB) - { - result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); - } - - CORE.Window.prevFB = fb; - - if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); - - CORE.Window.prevBO = bo; - -#endif // PLATFORM_DRM -#endif // PLATFORM_ANDROID || PLATFORM_DRM -} - -// Register all input events -void PollInputEvents(void) -{ -#if defined(SUPPORT_GESTURES_SYSTEM) - // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame - UpdateGestures(); -#endif - - // Reset keys/chars pressed registered - CORE.Input.Keyboard.keyPressedQueueCount = 0; - CORE.Input.Keyboard.charPressedQueueCount = 0; - // Reset key repeats - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - - // Reset last gamepad button/axis registered state - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; - -#if defined(PLATFORM_DRM) - // Register previous keys states - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) - { - CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - PollKeyboardEvents(); - - // Register previous mouse states - CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; - CORE.Input.Mouse.eventWheelMove = (Vector2){ 0.0f, 0.0f }; - for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) - { - CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; - CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i]; - } - - // Register gamepads buttons events - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (CORE.Input.Gamepad.ready[i]) - { - // Register previous gamepad states - for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; - } - } -#endif - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) - - // Register previous keys states - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) - { - CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - // Register previous mouse states - for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; - - // Register previous mouse wheel state - CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; - - // Register previous mouse position - CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; -#endif - - // Register previous touch states - for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; - - // Reset touch positions - // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, - // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! - //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; - -#if defined(PLATFORM_DESKTOP) - // Check if gamepads are ready - // NOTE: We do it here in case of disconnection - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; - else CORE.Input.Gamepad.ready[i] = false; - } - - // Register gamepads buttons events - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available - { - // Register previous gamepad states - for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; - - // Get current gamepad state - // NOTE: There is no callback available, so we get it manually - GLFWgamepadstate state = { 0 }; - glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller - - const unsigned char *buttons = state.buttons; - - for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) - { - int button = -1; // GamepadButton enum values assigned - - switch (k) - { - case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; - case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; - - case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - - case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; - case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break; - case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; - - case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; - case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - - case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break; - case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; - default: break; - } - - if (button != -1) // Check for valid button - { - if (buttons[k] == GLFW_PRESS) - { - CORE.Input.Gamepad.currentButtonState[i][button] = 1; - CORE.Input.Gamepad.lastButtonPressed = button; - } - else CORE.Input.Gamepad.currentButtonState[i][button] = 0; - } - } - - // Get current axis state - const float *axes = state.axes; - - for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) - { - CORE.Input.Gamepad.axisState[i][k] = axes[k]; - } - - // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis) - CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f); - CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f); - - CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1; - } - } - - CORE.Window.resizedLastFrame = false; - - if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) - else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) -#endif // PLATFORM_DESKTOP - -#if defined(PLATFORM_WEB) - CORE.Window.resizedLastFrame = false; -#endif // PLATFORM_WEB - -// Gamepad support using emscripten API -// NOTE: GLFW3 joystick functionality not available in web -#if defined(PLATFORM_WEB) - // Get number of gamepads connected - int numGamepads = 0; - if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads(); - - for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++) - { - // Register previous gamepad button states - for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; - - EmscriptenGamepadEvent gamepadState; - - int result = emscripten_get_gamepad_status(i, &gamepadState); - - if (result == EMSCRIPTEN_RESULT_SUCCESS) - { - // Register buttons data for every connected gamepad - for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++) - { - GamepadButton button = -1; - - // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface - switch (j) - { - case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; - case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; - case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; - case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; - case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; - case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; - case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break; - case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; - case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; - default: break; - } - - if (button != -1) // Check for valid button - { - if (gamepadState.digitalButton[j] == 1) - { - CORE.Input.Gamepad.currentButtonState[i][button] = 1; - CORE.Input.Gamepad.lastButtonPressed = button; - } - else CORE.Input.Gamepad.currentButtonState[i][button] = 0; - } - - //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); - } - - // Register axis data for every connected gamepad - for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++) - { - CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; - } - - CORE.Input.Gamepad.axisCount = gamepadState.numAxes; - } - } -#endif - -#if defined(PLATFORM_ANDROID) - // Register previous keys states - // NOTE: Android supports up to 260 keys - for (int i = 0; i < 260; i++) - { - CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Poll Events (registered events) - // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled) - while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) - { - // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); - - // NOTE: Never close window, native activity is controlled by the system! - if (CORE.Android.app->destroyRequested != 0) - { - //CORE.Window.shouldClose = true; - //ANativeActivity_finish(CORE.Android.app->activity); - } - } -#endif - -#if defined(PLATFORM_DRM) && defined(SUPPORT_SSH_KEYBOARD_RPI) - // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. - // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console - - if (!CORE.Input.Keyboard.evtMode) ProcessKeyboard(); - - // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread() - // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() -#endif -} - // Scan all files and directories in a base path // WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths @@ -5549,1638 +2438,6 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath); } -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) -// GLFW3 Error Callback, runs on GLFW3 error -static void ErrorCallback(int error, const char *description) -{ - TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description); -} - -// GLFW3 WindowSize Callback, runs when window is resizedLastFrame -// NOTE: Window resizing not allowed by default -static void WindowSizeCallback(GLFWwindow *window, int width, int height) -{ - // Reset viewport and projection matrix for new size - SetupViewport(width, height); - - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - CORE.Window.resizedLastFrame = true; - - if (IsWindowFullscreen()) return; - - // Set current screen size -#if defined(__APPLE__) - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; -#else - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - Vector2 windowScaleDPI = GetWindowScaleDPI(); - - CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x); - CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y); - } - else - { - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - } -#endif - - // NOTE: Postprocessing texture is not scaled to new size -} - -// GLFW3 WindowIconify Callback, runs when window is minimized/restored -static void WindowIconifyCallback(GLFWwindow *window, int iconified) -{ - if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified - else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored -} - -#if !defined(PLATFORM_WEB) -// GLFW3 WindowMaximize Callback, runs when window is maximized/restored -static void WindowMaximizeCallback(GLFWwindow *window, int maximized) -{ - if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized - else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored -} -#endif - -// GLFW3 WindowFocus Callback, runs when window get/lose focus -static void WindowFocusCallback(GLFWwindow *window, int focused) -{ - if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused - else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus -} - -// GLFW3 Keyboard Callback, runs on key pressed -static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) -{ - if (key < 0) return; // Security check, macOS fn key generates -1 - - // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 - // to work properly with our implementation (IsKeyDown/IsKeyUp checks) - if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; - else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; - else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; - -#if !defined(PLATFORM_WEB) - // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys - if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) || - ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1; -#endif - - // Check if there is space available in the key queue - if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) - { - // Add character to the queue - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; - CORE.Input.Keyboard.keyPressedQueueCount++; - } - - // Check the exit key to set close window - if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); - -#if defined(SUPPORT_SCREEN_CAPTURE) - if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) - { -#if defined(SUPPORT_GIF_RECORDING) - if (mods & GLFW_MOD_CONTROL) - { - if (gifRecording) - { - gifRecording = false; - - MsfGifResult result = msf_gif_end(&gifState); - - SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); - msf_gif_free(result); - - #if defined(PLATFORM_WEB) - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); - #endif - - TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); - } - else - { - gifRecording = true; - gifFrameCounter = 0; - - Vector2 scale = GetWindowScaleDPI(); - msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - screenshotCounter++; - - TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); - } - } - else -#endif // SUPPORT_GIF_RECORDING - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - } -#endif // SUPPORT_SCREEN_CAPTURE - -#if defined(SUPPORT_EVENTS_AUTOMATION) - if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) - { - eventsRecording = !eventsRecording; - - // On finish recording, we export events into a file - if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); - } - else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) - { - LoadAutomationEvents("eventsrec.rep"); - eventsPlaying = true; - - TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); - } -#endif -} - -// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) -static void CharCallback(GLFWwindow *window, unsigned int key) -{ - //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key); - - // NOTE: Registers any key down considering OS keyboard layout but - // does not detect action events, those should be managed by user... - // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907 - // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char - - // Check if there is space available in the queue - if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) - { - // Add character to the queue - CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; - CORE.Input.Keyboard.charPressedQueueCount++; - } -} - -// GLFW3 Mouse Button Callback, runs on mouse button pressed -static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) -{ - // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now, - // but future releases may add more actions (i.e. GLFW_REPEAT) - CORE.Input.Mouse.currentButtonState[button] = action; - -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP - // Process mouse events as touches to be able to use mouse-gestures - GestureEvent gestureEvent = { 0 }; - - // Register touch actions - if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN; - else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP; - - // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback() - - // Assign a pointer ID - gestureEvent.pointId[0] = 0; - - // Register touch points count - gestureEvent.pointCount = 1; - - // Register touch points position, only one point registered - gestureEvent.position[0] = GetMousePosition(); - - // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - - // Gesture data is sent to gestures-system for processing -#if defined(PLATFORM_WEB) - // Prevent calling ProcessGestureEvent() when Emscripten is present and there's a touch gesture, so EmscriptenTouchCallback() can handle it itself - if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent); -#else - ProcessGestureEvent(gestureEvent); -#endif - -#endif -} - -// GLFW3 Cursor Position Callback, runs on mouse move -static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) -{ - CORE.Input.Mouse.currentPosition.x = (float)x; - CORE.Input.Mouse.currentPosition.y = (float)y; - CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; - -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP - // Process mouse events as touches to be able to use mouse-gestures - GestureEvent gestureEvent = { 0 }; - - gestureEvent.touchAction = TOUCH_ACTION_MOVE; - - // Assign a pointer ID - gestureEvent.pointId[0] = 0; - - // Register touch points count - gestureEvent.pointCount = 1; - - // Register touch points position, only one point registered - gestureEvent.position[0] = CORE.Input.Touch.position[0]; - - // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - - // Gesture data is sent to gestures-system for processing - ProcessGestureEvent(gestureEvent); -#endif -} - -// GLFW3 Scrolling Callback, runs on mouse wheel -static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) -{ - CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; -} - -// GLFW3 CursorEnter Callback, when cursor enters the window -static void CursorEnterCallback(GLFWwindow *window, int enter) -{ - if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; - else CORE.Input.Mouse.cursorOnScreen = false; -} - -// GLFW3 Window Drop Callback, runs when drop files into window -static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) -{ - if (count > 0) - { - // In case previous dropped filepaths have not been freed, we free them - if (CORE.Window.dropFileCount > 0) - { - for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); - - RL_FREE(CORE.Window.dropFilepaths); - - CORE.Window.dropFileCount = 0; - CORE.Window.dropFilepaths = NULL; - } - - // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy - CORE.Window.dropFileCount = count; - CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); - - for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) - { - CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - strcpy(CORE.Window.dropFilepaths[i], paths[i]); - } - } -} -#endif - -#if defined(PLATFORM_ANDROID) -// ANDROID: Process activity lifecycle commands -static void AndroidCommandCallback(struct android_app *app, int32_t cmd) -{ - switch (cmd) - { - case APP_CMD_START: - { - //rendering = true; - } break; - case APP_CMD_RESUME: break; - case APP_CMD_INIT_WINDOW: - { - if (app->window != NULL) - { - if (CORE.Android.contextRebindRequired) - { - // Reset screen scaling to full display size - EGLint displayFormat = 0; - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - - // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the - // context rebinding if the screen is scaled unless offsets are added. There's probably a more - // appropriate way to fix this - ANativeWindow_setBuffersGeometry(app->window, - CORE.Window.render.width + CORE.Window.renderOffset.x, - CORE.Window.render.height + CORE.Window.renderOffset.y, - displayFormat); - - // Recreate display surface and re-attach OpenGL context - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); - eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context); - - CORE.Android.contextRebindRequired = false; - } - else - { - CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window); - CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window); - - // Initialize graphics device (display device and OpenGL context) - InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); - - // Initialize hi-res timer - InitTimer(); - - // Initialize random seed - srand((unsigned int)time(NULL)); - - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - Rectangle rec = GetFontDefault().recs[95]; - // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering - #if defined(SUPPORT_MODULE_RSHAPES) - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes - #endif - #endif - - // TODO: GPU assets reload in case of lost focus (lost context) - // NOTE: This problem has been solved just unbinding and rebinding context from display - /* - if (assetsReloadRequired) - { - for (int i = 0; i < assetCount; i++) - { - // TODO: Unload old asset if required - - // Load texture again to pointed texture - (*textureAsset + i) = LoadTexture(assetPath[i]); - } - } - */ - } - } - } break; - case APP_CMD_GAINED_FOCUS: - { - CORE.Android.appEnabled = true; - //ResumeMusicStream(); - } break; - case APP_CMD_PAUSE: break; - case APP_CMD_LOST_FOCUS: - { - CORE.Android.appEnabled = false; - //PauseMusicStream(); - } break; - case APP_CMD_TERM_WINDOW: - { - // Detach OpenGL context and destroy display surface - // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming. - // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) - // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :( - if (CORE.Window.device != EGL_NO_DISPLAY) - { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (CORE.Window.surface != EGL_NO_SURFACE) - { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; - } - - CORE.Android.contextRebindRequired = true; - } - // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' - // this means that the user has already called 'CloseWindow()' - - } break; - case APP_CMD_SAVE_STATE: break; - case APP_CMD_STOP: break; - case APP_CMD_DESTROY: break; - case APP_CMD_CONFIG_CHANGED: - { - //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); - //print_cur_config(CORE.Android.app); - - // Check screen orientation here! - } break; - default: break; - } -} - -static GamepadButton AndroidTranslateGamepadButton(int button) -{ - switch (button) - { - case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN; - case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; - case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT; - case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP; - case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1; - case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1; - case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2; - case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2; - case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB; - case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB; - case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT; - case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT; - case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE; - // On some (most?) gamepads dpad events are reported as axis motion instead - case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN; - case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT; - case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT; - case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP; - default: return GAMEPAD_BUTTON_UNKNOWN; - } -} - -// ANDROID: Get input events -static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) -{ - // If additional inputs are required check: - // https://developer.android.com/ndk/reference/group/input - // https://developer.android.com/training/game-controllers/controller-input - - int type = AInputEvent_getType(event); - int source = AInputEvent_getSource(event); - - if (type == AINPUT_EVENT_TYPE_MOTION) - { - if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || - ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) - { - // For now we'll assume a single gamepad which we "detect" on its input event - CORE.Input.Gamepad.ready[0] = true; - - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_X, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_Y, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_Z, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_RZ, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f; - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f; - - // dpad is reported as an axis on android - float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); - float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0); - - if (dpadX == 1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; - } - else if (dpadX == -1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1; - } - else - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; - } - - if (dpadY == 1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; - } - else if (dpadY == -1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1; - } - else - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; - } - - return 1; // Handled gamepad axis motion - } - } - else if (type == AINPUT_EVENT_TYPE_KEY) - { - int32_t keycode = AKeyEvent_getKeyCode(event); - //int32_t AKeyEvent_getMetaState(event); - - // Handle gamepad button presses and releases - if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || - ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) - { - // For now we'll assume a single gamepad which we "detect" on its input event - CORE.Input.Gamepad.ready[0] = true; - - GamepadButton button = AndroidTranslateGamepadButton(keycode); - - if (button == GAMEPAD_BUTTON_UNKNOWN) return 1; - - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) - { - CORE.Input.Gamepad.currentButtonState[0][button] = 1; - } - else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up - - return 1; // Handled gamepad button - } - - // Save current button and its state - // NOTE: Android key action is 0 for down and 1 for up - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) - { - CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; - CORE.Input.Keyboard.keyPressedQueueCount++; - } - else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[keycode] = 1; - else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up - - if (keycode == AKEYCODE_POWER) - { - // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS - // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS - // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected. - // NOTE: AndroidManifest.xml must have - // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour - return 0; - } - else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU)) - { - // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS! - return 1; - } - else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN)) - { - // Set default OS behaviour - return 0; - } - - return 0; - } - - // Register touch points count - CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event); - - for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - // Register touch points id - CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i); - - // Register touch points position - CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; - - // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height - float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x) / (float)CORE.Window.display.width; - float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y) / (float)CORE.Window.display.height; - CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x * widthRatio - (float)CORE.Window.renderOffset.x / 2; - CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y * heightRatio - (float)CORE.Window.renderOffset.y / 2; - } - - int32_t action = AMotionEvent_getAction(event); - unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; - -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID - GestureEvent gestureEvent = { 0 }; - - gestureEvent.pointCount = CORE.Input.Touch.pointCount; - - // Register touch actions - if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN; - else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP; - else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; - else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; - - for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; - gestureEvent.position[i] = CORE.Input.Touch.position[i]; - } - - // Gesture data is sent to gestures system for processing - ProcessGestureEvent(gestureEvent); -#endif - - int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - - if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP) - { - // One of the touchpoints is released, remove it from touch point arrays - for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++) - { - CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; - CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; - } - - CORE.Input.Touch.pointCount--; - } - - // When all touchpoints are tapped and released really quickly, this event is generated - if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; - - if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; - else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; - - return 0; -} -#endif - -#if defined(PLATFORM_WEB) -// Register fullscreen change events -static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) -{ - // TODO: Implement EmscriptenFullscreenChangeCallback()? - - return 1; // The event was consumed by the callback handler -} - -// Register window resize event -static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData) -{ - // TODO: Implement EmscriptenWindowResizedCallback()? - - return 1; // The event was consumed by the callback handler -} - -EM_JS(int, GetWindowInnerWidth, (), { return window.innerWidth; }); -EM_JS(int, GetWindowInnerHeight, (), { return window.innerHeight; }); - -// Register DOM element resize event -static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData) -{ - // Don't resize non-resizeable windows - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1; - - // This event is called whenever the window changes sizes, - // so the size of the canvas object is explicitly retrieved below - int width = GetWindowInnerWidth(); - int height = GetWindowInnerHeight(); - - if (width < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; - else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width; - - if (height < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; - else if (height > CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height; - - emscripten_set_canvas_element_size("#canvas",width,height); - - SetupViewport(width, height); // Reset viewport and projection matrix for new size - - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - CORE.Window.resizedLastFrame = true; - - if (IsWindowFullscreen()) return 1; - - // Set current screen size - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - - // NOTE: Postprocessing texture is not scaled to new size - - return 0; -} - -// Register mouse input events -static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) -{ - // This is only for registering mouse click events with emscripten and doesn't need to do anything - - return 1; // The event was consumed by the callback handler -} - -// Register connected/disconnected gamepads events -static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) -{ - /* - TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", - eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", - gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); - - for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]); - for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); - */ - - if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) - { - CORE.Input.Gamepad.ready[gamepadEvent->index] = true; - sprintf(CORE.Input.Gamepad.name[gamepadEvent->index],"%s",gamepadEvent->id); - } - else CORE.Input.Gamepad.ready[gamepadEvent->index] = false; - - return 1; // The event was consumed by the callback handler -} - -// Register touch input events -static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) -{ - // Register touch points count - CORE.Input.Touch.pointCount = touchEvent->numTouches; - - double canvasWidth = 0.0; - double canvasHeight = 0.0; - // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but - // we are looking for actual CSS size: canvas.style.width and canvas.style.height - //EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight); - emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight); - - for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - // Register touch points id - CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier; - - // Register touch points position - CORE.Input.Touch.position[i] = (Vector2){ touchEvent->touches[i].targetX, touchEvent->touches[i].targetY }; - - // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height - CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth); - CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight); - - if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1; - else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0; - } - -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB - GestureEvent gestureEvent = { 0 }; - - gestureEvent.pointCount = CORE.Input.Touch.pointCount; - - // Register touch actions - if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN; - else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP; - else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; - else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; - - for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; - gestureEvent.position[i] = CORE.Input.Touch.position[i]; - - // Normalize gestureEvent.position[i] - gestureEvent.position[i].x /= (float)GetScreenWidth(); - gestureEvent.position[i].y /= (float)GetScreenHeight(); - } - - // Gesture data is sent to gestures system for processing - ProcessGestureEvent(gestureEvent); - - // Reset the pointCount for web, if it was the last Touch End event - if (eventType == EMSCRIPTEN_EVENT_TOUCHEND && CORE.Input.Touch.pointCount == 1) CORE.Input.Touch.pointCount = 0; -#endif - - return 1; // The event was consumed by the callback handler -} -#endif - -#if defined(PLATFORM_DRM) -// Initialize Keyboard system (using standard input) -static void InitKeyboard(void) -{ - // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor, - // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE - - // Save terminal keyboard settings - tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings); - - // Reconfigure terminal with new settings - struct termios keyboardNewSettings = { 0 }; - keyboardNewSettings = CORE.Input.Keyboard.defaultSettings; - - // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing - // NOTE: ISIG controls if ^C and ^Z generate break signals or not - keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG); - //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF); - keyboardNewSettings.c_cc[VMIN] = 1; - keyboardNewSettings.c_cc[VTIME] = 0; - - // Set new keyboard settings (change occurs immediately) - tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings); - - // Save old keyboard mode to restore it at the end - CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified - - // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno) - int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode); - - // In case of failure, it could mean a remote keyboard is used (SSH) - if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used"); - else - { - // Reconfigure keyboard mode to get: - // - scancodes (K_RAW) - // - keycodes (K_MEDIUMRAW) - // - ASCII chars (K_XLATE) - // - UNICODE chars (K_UNICODE) - ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); // ASCII chars - } - - // Register keyboard restore when program finishes - atexit(RestoreKeyboard); -} - -// Restore default keyboard input -static void RestoreKeyboard(void) -{ - // Reset to default keyboard settings - tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings); - - // Reconfigure keyboard to default mode - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags); - ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode); -} - -#if defined(SUPPORT_SSH_KEYBOARD_RPI) -// Process keyboard inputs -static void ProcessKeyboard(void) -{ - #define MAX_KEYBUFFER_SIZE 32 // Max size in bytes to read - - // Keyboard input polling (fill keys[256] array with status) - int bufferByteCount = 0; // Bytes available on the buffer - char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 }; // Max keys to be read at a time - - // Read availables keycodes from stdin - bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE); // POSIX system call - - // Reset pressed keys array (it will be filled below) - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) - { - CORE.Input.Keyboard.currentKeyState[i] = 0; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - // Fill all read bytes (looking for keys) - for (int i = 0; i < bufferByteCount; i++) - { - // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code! - // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42 - if (keysBuffer[i] == 0x1b) - { - // Check if ESCAPE key has been pressed to stop program - if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1; - else - { - if (keysBuffer[i + 1] == 0x5b) // Special function key - { - if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) - { - // Process special function keys (F1 - F12) - switch (keysBuffer[i + 3]) - { - case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break; // raylib KEY_F1 - case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break; // raylib KEY_F2 - case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break; // raylib KEY_F3 - case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break; // raylib KEY_F4 - case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break; // raylib KEY_F5 - case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break; // raylib KEY_F6 - case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break; // raylib KEY_F7 - case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break; // raylib KEY_F8 - case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break; // raylib KEY_F9 - case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break; // raylib KEY_F10 - case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break; // raylib KEY_F11 - case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break; // raylib KEY_F12 - default: break; - } - - if (keysBuffer[i + 2] == 0x5b) i += 4; - else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5; - } - else - { - switch (keysBuffer[i + 2]) - { - case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break; // raylib KEY_UP - case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break; // raylib KEY_DOWN - case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break; // raylib KEY_RIGHT - case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break; // raylib KEY_LEFT - default: break; - } - - i += 3; // Jump to next key - } - - // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT) - } - } - } - else if (keysBuffer[i] == 0x0a) // raylib KEY_ENTER (don't mix with KEY_*) - { - CORE.Input.Keyboard.currentKeyState[257] = 1; - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue - CORE.Input.Keyboard.keyPressedQueueCount++; - } - else if (keysBuffer[i] == 0x7f) // raylib KEY_BACKSPACE - { - CORE.Input.Keyboard.currentKeyState[259] = 1; - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue - CORE.Input.Keyboard.keyPressedQueueCount++; - } - else - { - // Translate lowercase a-z letters to A-Z - if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122)) - { - CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1; - } - else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1; - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i]; // Add keys pressed into queue - CORE.Input.Keyboard.keyPressedQueueCount++; - } - } - - // Check exit key (same functionality as GLFW3 KeyCallback()) - if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; - -#if defined(SUPPORT_SCREEN_CAPTURE) - // Check screen capture key (raylib key: KEY_F12) - if (CORE.Input.Keyboard.currentKeyState[301] == 1) - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } -#endif -} -#endif // SUPPORT_SSH_KEYBOARD_RPI - -// Initialise user input from evdev(/dev/input/event) this means mouse, keyboard or gamepad devices -static void InitEvdevInput(void) -{ - char path[MAX_FILEPATH_LENGTH] = { 0 }; - DIR *directory = NULL; - struct dirent *entity = NULL; - - // Initialise keyboard file descriptor - CORE.Input.Keyboard.fd = -1; - - // Reset variables - for (int i = 0; i < MAX_TOUCH_POINTS; ++i) - { - CORE.Input.Touch.position[i].x = -1; - CORE.Input.Touch.position[i].y = -1; - } - - // Reset keyboard key state - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) - { - CORE.Input.Keyboard.currentKeyState[i] = 0; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - // Open the linux directory of "/dev/input" - directory = opendir(DEFAULT_EVDEV_PATH); - - if (directory) - { - while ((entity = readdir(directory)) != NULL) - { - if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*" - (strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*" - { - sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); - ConfigureEvdevDevice(path); // Configure the device if appropriate - } - } - - closedir(directory); - } - else TRACELOG(LOG_WARNING, "RPI: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH); -} - -// Identifies a input device and configures it for use if appropriate -static void ConfigureEvdevDevice(char *device) -{ - #define BITS_PER_LONG (8*sizeof(long)) - #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1) - #define OFF(x) ((x)%BITS_PER_LONG) - #define BIT(x) (1UL<> OFF(bit)) & 1) - - struct input_absinfo absinfo = { 0 }; - unsigned long evBits[NBITS(EV_MAX)] = { 0 }; - unsigned long absBits[NBITS(ABS_MAX)] = { 0 }; - unsigned long relBits[NBITS(REL_MAX)] = { 0 }; - unsigned long keyBits[NBITS(KEY_MAX)] = { 0 }; - bool hasAbs = false; - bool hasRel = false; - bool hasAbsMulti = false; - int freeWorkerId = -1; - int fd = -1; - - InputEventWorker *worker = NULL; - - // Open the device and allocate worker - //------------------------------------------------------------------------------------------------------- - // Find a free spot in the workers array - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].threadId == 0) - { - freeWorkerId = i; - break; - } - } - - // Select the free worker from array - if (freeWorkerId >= 0) - { - worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker - memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker - } - else - { - TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device); - return; - } - - // Open the device - fd = open(device, O_RDONLY | O_NONBLOCK); - if (fd < 0) - { - TRACELOG(LOG_WARNING, "RPI: Failed to open input device: %s", device); - return; - } - worker->fd = fd; - - // Grab number on the end of the devices name "event" - int devNum = 0; - char *ptrDevName = strrchr(device, 't'); - worker->eventNum = -1; - - if (ptrDevName != NULL) - { - if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum; - } - else worker->eventNum = 0; // TODO: HACK: Grab number for mouse0 device! - - // At this point we have a connection to the device, but we don't yet know what the device is. - // It could be many things, even as simple as a power button... - //------------------------------------------------------------------------------------------------------- - - // Identify the device - //------------------------------------------------------------------------------------------------------- - ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties - - // Check for absolute input devices - if (TEST_BIT(evBits, EV_ABS)) - { - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); - - // Check for absolute movement support (usually touchscreens, but also joysticks) - if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) - { - hasAbs = true; - - // Get the scaling values - ioctl(fd, EVIOCGABS(ABS_X), &absinfo); - worker->absRange.x = absinfo.minimum; - worker->absRange.width = absinfo.maximum - absinfo.minimum; - ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); - worker->absRange.y = absinfo.minimum; - worker->absRange.height = absinfo.maximum - absinfo.minimum; - } - - // Check for multiple absolute movement support (usually multitouch touchscreens) - if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) - { - hasAbsMulti = true; - - // Get the scaling values - ioctl(fd, EVIOCGABS(ABS_X), &absinfo); - worker->absRange.x = absinfo.minimum; - worker->absRange.width = absinfo.maximum - absinfo.minimum; - ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); - worker->absRange.y = absinfo.minimum; - worker->absRange.height = absinfo.maximum - absinfo.minimum; - } - } - - // Check for relative movement support (usually mouse) - if (TEST_BIT(evBits, EV_REL)) - { - ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); - - if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; - } - - // Check for button support to determine the device type(usually on all input devices) - if (TEST_BIT(evBits, EV_KEY)) - { - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); - - if (hasAbs || hasAbsMulti) - { - if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen - if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet - if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet - if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet - if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device - } - - if (hasRel) - { - if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse - if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse - } - - if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad - - if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard - } - //------------------------------------------------------------------------------------------------------- - - // Decide what to do with the device - //------------------------------------------------------------------------------------------------------- - if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1)) - { - // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a - // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate - // threads so that they don't drop events when the frame rate is slow. - TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); - CORE.Input.Keyboard.fd = worker->fd; - } - else if (worker->isTouch || worker->isMouse) - { - // Looks like an interesting device - TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device, - worker->isMouse? "mouse " : "", - worker->isMultitouch? "multitouch " : "", - worker->isTouch? "touchscreen " : "", - worker->isGamepad? "gamepad " : ""); - - // Create a thread for this device - int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker); - if (error != 0) - { - TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error); - worker->threadId = 0; - close(fd); - } - -#if defined(USE_LAST_TOUCH_DEVICE) - // Find touchscreen with the highest index - int maxTouchNumber = -1; - - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; - } - - // Find touchscreens with lower indexes - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) - { - if (CORE.Input.eventWorker[i].threadId != 0) - { - TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i); - pthread_cancel(CORE.Input.eventWorker[i].threadId); - close(CORE.Input.eventWorker[i].fd); - } - } - } -#endif - } - else close(fd); // We are not interested in this device - //------------------------------------------------------------------------------------------------------- -} - -static void PollKeyboardEvents(void) -{ - // Scancode to keycode mapping for US keyboards - // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard: - // Currently non US keyboards will have the wrong mapping for some keys - static const int keymapUS[] = { - 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, - 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96, - 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291, - 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325, - 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95, - 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, - 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, - 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, - 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 - }; - - int fd = CORE.Input.Keyboard.fd; - if (fd == -1) return; - - struct input_event event = { 0 }; - int keycode = -1; - - // Try to read data from the keyboard and only continue if successful - while (read(fd, &event, sizeof(event)) == (int)sizeof(event)) - { - // Button parsing - if (event.type == EV_KEY) - { -#if defined(SUPPORT_SSH_KEYBOARD_RPI) - // Change keyboard mode to events - CORE.Input.Keyboard.evtMode = true; -#endif - // Keyboard button parsing - if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 - { - keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the appropriate keycode - - // Make sure we got a valid keycode - if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) - { - // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt - // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL, - // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat - CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0; - if (event.value >= 1) - { - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed - CORE.Input.Keyboard.keyPressedQueueCount++; - } - - #if defined(SUPPORT_SCREEN_CAPTURE) - // Check screen capture key (raylib key: KEY_F12) - if (CORE.Input.Keyboard.currentKeyState[301] == 1) - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - #endif - - if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; - - TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", event.value == 0 ? "UP":"DOWN", event.code, keycode); - } - } - } - } -} - -// Input device events reading thread -static void *EventThread(void *arg) -{ - struct input_event event = { 0 }; - InputEventWorker *worker = (InputEventWorker *)arg; - - int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE - bool gestureUpdate = false; // Flag to note gestures require to update - - while (!CORE.Window.shouldClose) - { - // Try to read data from the device and only continue if successful - while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) - { - // Relative movement parsing - if (event.type == EV_REL) - { - if (event.code == REL_X) - { - CORE.Input.Mouse.currentPosition.x += event.value; - CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - if (event.code == REL_Y) - { - CORE.Input.Mouse.currentPosition.y += event.value; - CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value; - } - - // Absolute movement parsing - if (event.type == EV_ABS) - { - // Basic movement - if (event.code == ABS_X) - { - CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange - CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - if (event.code == ABS_Y) - { - CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange - CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - // Multitouch movement - if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remember the slot number for the folowing events - - if (event.code == ABS_MT_POSITION_X) - { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange - } - - if (event.code == ABS_MT_POSITION_Y) - { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange - } - - if (event.code == ABS_MT_TRACKING_ID) - { - if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS)) - { - // Touch has ended for this point - CORE.Input.Touch.position[worker->touchSlot].x = -1; - CORE.Input.Touch.position[worker->touchSlot].y = -1; - } - } - - // Touchscreen tap - if (event.code == ABS_PRESSURE) - { - int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; - - if (!event.value && previousMouseLeftButtonState) - { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; - - touchAction = 0; // TOUCH_ACTION_UP - gestureUpdate = true; - } - - if (event.value && !previousMouseLeftButtonState) - { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; - - touchAction = 1; // TOUCH_ACTION_DOWN - gestureUpdate = true; - } - } - - } - - // Button parsing - if (event.type == EV_KEY) - { - // Mouse button parsing - if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) - { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; - - if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN - else touchAction = 0; // TOUCH_ACTION_UP - gestureUpdate = true; - } - - if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; - if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; - if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; - if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; - if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; - if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value; - } - - // Screen confinement - if (!CORE.Input.Mouse.cursorHidden) - { - if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0; - 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; - - if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0; - 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; - } - - // Update touch point count - CORE.Input.Touch.pointCount = 0; - for (int i = 0; i < MAX_TOUCH_POINTS; i++) - { - if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++; - } - -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM - if (gestureUpdate) - { - GestureEvent gestureEvent = { 0 }; - - gestureEvent.touchAction = touchAction; - gestureEvent.pointCount = CORE.Input.Touch.pointCount; - - for (int i = 0; i < MAX_TOUCH_POINTS; i++) - { - gestureEvent.pointId[i] = i; - gestureEvent.position[i] = CORE.Input.Touch.position[i]; - } - - ProcessGestureEvent(gestureEvent); - } -#endif - } - - WaitTime(0.005); // Sleep for 5ms to avoid hogging CPU time - } - - close(worker->fd); - - return NULL; -} - -// Initialize gamepad system -static void InitGamepad(void) -{ - char gamepadDev[128] = { 0 }; - - for (int i = 0; i < MAX_GAMEPADS; i++) - { - sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i); - - if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) - { - // NOTE: Only show message for first gamepad - if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available"); - } - else - { - CORE.Input.Gamepad.ready[i] = true; - - // NOTE: Only create one thread - if (i == 0) - { - int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL); - - if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); - else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); - } - } - } -} - -// Process Gamepad (/dev/input/js0) -static void *GamepadThread(void *arg) -{ - #define JS_EVENT_BUTTON 0x01 // Button pressed/released - #define JS_EVENT_AXIS 0x02 // Joystick axis moved - #define JS_EVENT_INIT 0x80 // Initial state of device - - struct js_event { - unsigned int time; // event timestamp in milliseconds - short value; // event value - unsigned char type; // event type - unsigned char number; // event axis/button number - }; - - // Read gamepad event - struct js_event gamepadEvent = { 0 }; - - while (!CORE.Window.shouldClose) - { - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) - { - gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events - - // Process gamepad events by type - if (gamepadEvent.type == JS_EVENT_BUTTON) - { - //TRACELOG(LOG_WARNING, "RPI: Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value); - - if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS) - { - // 1 - button pressed, 0 - button released - CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value; - - if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number; - else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - } - } - else if (gamepadEvent.type == JS_EVENT_AXIS) - { - //TRACELOG(LOG_WARNING, "RPI: Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value); - - if (gamepadEvent.number < MAX_GAMEPAD_AXIS) - { - // NOTE: Scaling of gamepadEvent.value to get values between -1..1 - CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768; - } - } - } - else WaitTime(0.001); // Sleep for 1 ms to avoid hogging CPU time - } - } - - return NULL; -} -#endif // PLATFORM_DRM - -#if defined(PLATFORM_DRM) -// Search matching DRM mode in connector's mode list -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, "DISPLAY: DRM 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])) return i; - } - - return -1; - - #undef BINCMP -} - -// Search exactly matching DRM connector mode in connector's list -static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) -{ - 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"); - - 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, "DISPLAY: DRM 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)) continue; - - if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i; - } - - TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found"); - return -1; -} - -// Search the nearest matching DRM connector mode in connector's list -static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) -{ - 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"); - - 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, "DISPLAY: DRM 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)) - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small"); - continue; - } - - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode"); - continue; - } - - if (nearestIndex < 0) - { - nearestIndex = i; - continue; - } - - const int widthDiff = abs(mode->hdisplay - width); - const int heightDiff = abs(mode->vdisplay - height); - const int fpsDiff = abs(mode->vrefresh - fps); - - const int nearestWidthDiff = abs(CORE.Window.connector->modes[nearestIndex].hdisplay - width); - const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height); - const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps); - - if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) { - nearestIndex = i; - } - } - - return nearestIndex; -} -#endif - #if defined(SUPPORT_EVENTS_AUTOMATION) // NOTE: Loading happens over AutomationEvent *events // TODO: This system should probably be redesigned diff --git a/src/rcore.h b/src/rcore.h new file mode 100644 index 000000000..5d9e3bd74 --- /dev/null +++ b/src/rcore.h @@ -0,0 +1,318 @@ +/********************************************************************************************** +* +* rcore - Common types and globals (all platforms) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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. +* +**********************************************************************************************/ + +#ifndef RCORE_H +#define RCORE_H + +#include // Required for: srand(), rand(), atexit() +#include // Required for: sprintf() [Used in OpenURL()] +#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: time() [Used in InitTimer()] +#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] + +#include "utils.h" // Required for: TRACELOG() macros + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) + #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management + // NOTE: GLFW3 already includes gl.h (OpenGL) headers +#endif + +#if defined(PLATFORM_ANDROID) + #include // Native platform windowing system interface + //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) +#endif + +#if defined(PLATFORM_DRM) + + #include // POSIX file control definitions - open(), creat(), fcntl() + #include // POSIX standard function definitions - read(), close(), STDIN_FILENO + #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() + #include // POSIX threads management (inputs reading) + #include // POSIX directory browsing + + #include // Required for: ioctl() - UNIX System call for device-specific input/output operations + #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition + #include // Linux: Keycodes constants definition (KEY_A, ...) + #include // Linux: Joystick support library + + #include // Generic Buffer Management (native platform for EGL on DRM) + #include // Direct Rendering Manager user-level library interface + #include // Direct Rendering Manager mode setting (KMS) interface + + #include "EGL/egl.h" // Native platform windowing system interface + #include "EGL/eglext.h" // EGL extensions + + typedef struct + { + pthread_t threadId; // Event reading thread id + int fd; // File descriptor to the device it is assigned to + int eventNum; // Number of 'event' device + Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) + int touchSlot; // Hold the touch slot number of the currently being sent multitouch block + bool isMouse; // True if device supports relative X Y movements + bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH + bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH + bool isKeyboard; // True if device has letter keycodes + bool isGamepad; // True if device has gamepad buttons + } InputEventWorker; + +#endif + +// TODO: PROVIDE A HEADER TO BE USED BY ALL THE rcore_* IMPLEMENTATIONS + +#include "raylib.h" + +#include "rlgl.h" + +#define RAYMATH_IMPLEMENTATION +#include "raymath.h" + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#if defined(PLATFORM_DRM) + #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number + + #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) + #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events +#endif + +#ifndef MAX_FILEPATH_CAPACITY + #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath +#endif +#ifndef MAX_FILEPATH_LENGTH + #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) +#endif + +#ifndef MAX_KEYBOARD_KEYS + #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported +#endif +#ifndef MAX_MOUSE_BUTTONS + #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported +#endif +#ifndef MAX_GAMEPADS + #define MAX_GAMEPADS 4 // Maximum number of gamepads supported +#endif +#ifndef MAX_GAMEPAD_AXIS + #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) +#endif +#ifndef MAX_GAMEPAD_BUTTONS + #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) +#endif +#ifndef MAX_TOUCH_POINTS + #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported +#endif +#ifndef MAX_KEY_PRESSED_QUEUE + #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue +#endif +#ifndef MAX_CHAR_PRESSED_QUEUE + #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue +#endif + +#ifndef MAX_DECOMPRESSION_SIZE + #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB +#endif + +// Flags operation macros +#define FLAG_SET(n, f) ((n) |= (f)) +#define FLAG_CLEAR(n, f) ((n) &= ~(f)) +#define FLAG_TOGGLE(n, f) ((n) ^= (f)) +#define FLAG_CHECK(n, f) ((n) & (f)) + +// TODO: HACK: Added flag if not provided by GLFW when using external library +// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev +#if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +#endif + +#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { int x; int y; } Point; +typedef struct { unsigned int width; unsigned int height; } Size; + +// Core global state context data +typedef struct CoreData { + struct { +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + GLFWwindow *handle; // GLFW window handle (graphic device) +#endif +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) + int fd; // File descriptor for /dev/dri/... + drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector + drmModeCrtc *crtc; // CRT Controller + int modeIndex; // Index of the used mode of connector->modes + struct gbm_device *gbmDevice; // GBM device + struct gbm_surface *gbmSurface; // GBM surface + struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) + uint32_t prevFB; // Previous GBM framebufer (during frame swapping) +#endif // PLATFORM_DRM + 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 + EGLConfig config; // Graphic config +#endif + const char *title; // Window text title const pointer + unsigned int flags; // Configuration flags (bit based), keeps window state + bool ready; // Check if window has been initialized successfully + bool fullscreen; // Check if fullscreen mode is enabled + bool shouldClose; // Check if window set for closing + bool resizedLastFrame; // Check if window has been resized last frame + bool eventWaiting; // Wait for events before ending frame + + Point position; // Window position (required on fullscreen toggle) + Point previousPosition; // Window previous position (required on borderless windowed toggle) + Size display; // Display width and height (monitor, device-screen, LCD, ...) + Size screen; // Screen width and height (used render area) + Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) + Size currentFbo; // Current render width and height (depends on active fbo) + Size render; // Framebuffer width and height (render area, including black bars if required) + Point renderOffset; // Offset from render area (must be divided by 2) + Size screenMin; // Screen minimum width and height (for resizable window) + Size screenMax; // Screen maximum width and height (for resizable window) + Size windowMin; // Window minimum width and height + Size windowMax; // Window maximum width and height + Matrix screenScale; // Matrix to scale screen (framebuffer rendering) + + char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) + unsigned int dropFileCount; // Count dropped files strings + + } Window; +#if defined(PLATFORM_ANDROID) + struct { + bool appEnabled; // Flag to detect if app is active ** = true + struct android_app *app; // Android activity + struct android_poll_source *source; // Android events polling source + bool contextRebindRequired; // Used to know context rebind required + } Android; +#endif + struct { + const char *basePath; // Base path for data storage + } Storage; + struct { +#if defined(PLATFORM_DRM) + InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" +#endif + struct { + int exitKey; // Default exit key + char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state + char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially + char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. + + int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue + int keyPressedQueueCount; // Input keys queue count + + int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) + int charPressedQueueCount; // Input characters queue count + +#if defined(PLATFORM_DRM) + int defaultMode; // Default keyboard mode +#if defined(SUPPORT_SSH_KEYBOARD_RPI) + bool evtMode; // Keyboard in event mode +#endif + int defaultFileFlags; // Default IO file flags + struct termios defaultSettings; // Default keyboard settings + int fd; // File descriptor for the evdev keyboard +#endif + } Keyboard; + struct { + Vector2 offset; // Mouse offset + Vector2 scale; // Mouse scaling + Vector2 currentPosition; // Mouse position on screen + Vector2 previousPosition; // Previous mouse position + + int cursor; // Tracks current mouse cursor + bool cursorHidden; // Track if cursor is hidden + bool cursorOnScreen; // Tracks if cursor is inside client area + + char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state + char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state + Vector2 currentWheelMove; // Registers current mouse wheel variation + Vector2 previousWheelMove; // Registers previous mouse wheel variation +#if defined(PLATFORM_DRM) + Vector2 eventWheelMove; // Registers the event mouse wheel variation + // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update + char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab +#endif + } Mouse; + struct { + int pointCount; // Number of touch points active + int pointId[MAX_TOUCH_POINTS]; // Point identifiers + Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen + char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state + char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state + } Touch; + struct { + int lastButtonPressed; // Register last gamepad button pressed + int axisCount; // Register number of available gamepad axis + bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready + char name[MAX_GAMEPADS][64]; // Gamepad name holder + char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state + char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state + float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state +#if defined(PLATFORM_DRM) + pthread_t threadId; // Gamepad reading thread id + int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor +#endif + } Gamepad; + } Input; + struct { + double current; // Current time measure + double previous; // Previous time measure + double update; // Time measure for frame update + 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_DRM) + unsigned long long int base; // Base time measure for hi-res timer +#endif + unsigned int frameCounter; // Frame counter + } Time; +} CoreData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; + +#endif diff --git a/src/rcore_android.c b/src/rcore_android.c new file mode 100644 index 000000000..61d729a1c --- /dev/null +++ b/src/rcore_android.c @@ -0,0 +1,1267 @@ +/********************************************************************************************** +* +* rcore_android - Functions to manage window, graphics device and inputs +* +* PLATFORM: ANDROID +* - Android (ARM, ARM64) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_ANDROID_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_ANDROID -not used- +* +* DEPENDENCIES: +* Android NDK - Provides C API to access Android functionality +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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 "rcore.h" + +//#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) +#include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others +#include // Required for: android_app struct and activity management +#include // Required for: JNIEnv and JavaVM [Used in OpenURL()] + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands +static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs +static GamepadButton AndroidTranslateGamepadButton(int button); // Map Android gamepad button to raylib gamepad button + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// To allow easier porting to android, we allow the user to define a +// main function which we call from android_main, defined by ourselves +extern int main(int argc, char *argv[]); + +// Android main function +void android_main(struct android_app *app) +{ + char arg0[] = "raylib"; // NOTE: argv[] are mutable + CORE.Android.app = app; + + // NOTE: Return from main is ignored + (void)main(1, (char *[]) { arg0, NULL }); + + // Request to end the native activity + ANativeActivity_finish(app->activity); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Waiting for application events before complete finishing + while (!app->destroyRequested) + { + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) + { + if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); + } + } +} + +// NOTE: Add this to header (if apps really need it) +struct android_app *GetAndroidApp(void) +{ + return CORE.Android.app; +} + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + + // Set desired windows flags before initializing anything + ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + + int orientation = AConfiguration_getOrientation(CORE.Android.app->config); + + if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); + else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); + + // TODO: Automatic orientation doesn't seem to work + if (width <= height) + { + AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); + } + else + { + AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); + } + + //AConfiguration_getDensity(CORE.Android.app->config); + //AConfiguration_getKeyboard(CORE.Android.app->config); + //AConfiguration_getScreenSize(CORE.Android.app->config); + //AConfiguration_getScreenLong(CORE.Android.app->config); + + // Initialize App command system + // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... + CORE.Android.app->onAppCmd = AndroidCommandCallback; + + // Initialize input events system + CORE.Android.app->onInputEvent = AndroidInputCallback; + + // Initialize assets manager + InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath); + + // Initialize base path for storage + CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath; + + TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Wait for window to be initialized (display and context) + while (!CORE.Window.ready) + { + // Process events loop + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + { + // Process this event + if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + + // NOTE: Never close window, native activity is controlled by the system! + //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true; + } + } +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + + // Close surface, context and display + if (CORE.Window.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + if (CORE.Window.context != EGL_NO_CONTEXT) + { + eglDestroyContext(CORE.Window.device, CORE.Window.context); + CORE.Window.context = EGL_NO_CONTEXT; + } + + eglTerminate(CORE.Window.device); + CORE.Window.device = EGL_NO_DISPLAY; + } + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return CORE.Android.appEnabled; +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_ANDROID"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_ANDROID"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_ANDROID"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_ANDROID"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_ANDROID"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_ANDROID"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_ANDROID"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_ANDROID"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_ANDROID"); +} + +// 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 PLATFORM_ANDROID"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_ANDROID"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_ANDROID"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_ANDROID"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_ANDROID"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_ANDROID"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_ANDROID"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_ANDROID"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_ANDROID"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_ANDROID"); + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_ANDROID"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_ANDROID"); + return NULL; +} + +// 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; +} + +// 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; +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_ANDROID + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code on PLATFORM_ANDROID + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + JNIEnv *env = NULL; + JavaVM *vm = CORE.Android.app->activity->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + + jstring urlString = (*env)->NewStringUTF(env, url); + jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); + jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); + jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); + + jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); + jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); + jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); + jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); + jobject intent = (*env)->AllocObject(env, intentClass); + + (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); + jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); + jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); + (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent); + + (*vm)->DetachCurrentThread(vm); + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +void SetExitKey(int key) +{ + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_ANDROID"); +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_ANDROID"); + return NULL; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + return GetTouchPosition(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; +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_ANDROID"); + return 0.0f; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_ANDROID"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + 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); + + return position; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(CORE.Window.device, CORE.Window.surface); +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_ANDROID the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Register previous keys states + // NOTE: Android supports up to 260 keys + for (int i = 0; i < 260; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Poll Events (registered events) + // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled) + while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + { + // Process this event + if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + + // NOTE: Never close window, native activity is controlled by the system! + if (CORE.Android.app->destroyRequested != 0) + { + //CORE.Window.shouldClose = true; + //ANativeActivity_finish(CORE.Android.app->activity); + } + } +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + EGLint samples = 0; + EGLint sampleBuffer = 0; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + samples = 4; + sampleBuffer = 1; + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + } + + const EGLint framebufferAttribs[] = + { + EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support + 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_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 + EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA + EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) + EGL_NONE + }; + + const EGLint contextAttribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint numConfigs = 0; + + // Get an EGL device connection + CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (CORE.Window.device == EGL_NO_DISPLAY) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Initialize the EGL device connection + if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) + { + // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Get an appropriate EGL framebuffer configuration + eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs); + + // Set rendering API + eglBindAPI(EGL_OPENGL_ES_API); + + // Create an EGL rendering context + CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); + if (CORE.Window.context == EGL_NO_CONTEXT) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); + return false; + } + + // Create an EGL window surface + //--------------------------------------------------------------------------------- + EGLint displayFormat = 0; + + // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() + // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID + eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + + // 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); + + ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size + + CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); + + // There must be at least one frame displayed before the buffers are swapped + //eglSwapInterval(CORE.Window.device, 1); + + if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + return false; + } + else + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + CORE.Window.ready = true; + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// ANDROID: Process activity lifecycle commands +static void AndroidCommandCallback(struct android_app *app, int32_t cmd) +{ + switch (cmd) + { + case APP_CMD_START: + { + //rendering = true; + } break; + case APP_CMD_RESUME: break; + case APP_CMD_INIT_WINDOW: + { + if (app->window != NULL) + { + if (CORE.Android.contextRebindRequired) + { + // Reset screen scaling to full display size + EGLint displayFormat = 0; + eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + + // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the + // context rebinding if the screen is scaled unless offsets are added. There's probably a more + // appropriate way to fix this + ANativeWindow_setBuffersGeometry(app->window, + CORE.Window.render.width + CORE.Window.renderOffset.x, + CORE.Window.render.height + CORE.Window.renderOffset.y, + displayFormat); + + // Recreate display surface and re-attach OpenGL context + CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); + eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context); + + CORE.Android.contextRebindRequired = false; + } + else + { + CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window); + CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window); + + // Initialize graphics device (display device and OpenGL context) + InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + Rectangle rec = GetFontDefault().recs[95]; + // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + #if defined(SUPPORT_MODULE_RSHAPES) + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes + #endif + #endif + + // TODO: GPU assets reload in case of lost focus (lost context) + // NOTE: This problem has been solved just unbinding and rebinding context from display + /* + if (assetsReloadRequired) + { + for (int i = 0; i < assetCount; i++) + { + // TODO: Unload old asset if required + + // Load texture again to pointed texture + (*textureAsset + i) = LoadTexture(assetPath[i]); + } + } + */ + } + } + } break; + case APP_CMD_GAINED_FOCUS: + { + CORE.Android.appEnabled = true; + //ResumeMusicStream(); + } break; + case APP_CMD_PAUSE: break; + case APP_CMD_LOST_FOCUS: + { + CORE.Android.appEnabled = false; + //PauseMusicStream(); + } break; + case APP_CMD_TERM_WINDOW: + { + // Detach OpenGL context and destroy display surface + // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming. + // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) + // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :( + if (CORE.Window.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + CORE.Android.contextRebindRequired = true; + } + // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' + // this means that the user has already called 'CloseWindow()' + + } break; + case APP_CMD_SAVE_STATE: break; + case APP_CMD_STOP: break; + case APP_CMD_DESTROY: break; + case APP_CMD_CONFIG_CHANGED: + { + //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); + //print_cur_config(CORE.Android.app); + + // Check screen orientation here! + } break; + default: break; + } +} + +// ANDROID: Map Android gamepad button to raylib gamepad button +static GamepadButton AndroidTranslateGamepadButton(int button) +{ + switch (button) + { + case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN; + case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; + case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT; + case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP; + case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1; + case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1; + case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2; + case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB; + case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB; + case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT; + case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT; + case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE; + // On some (most?) gamepads dpad events are reported as axis motion instead + case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN; + case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT; + case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT; + case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP; + default: return GAMEPAD_BUTTON_UNKNOWN; + } +} + +// ANDROID: Get input events +static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) +{ + // If additional inputs are required check: + // https://developer.android.com/ndk/reference/group/input + // https://developer.android.com/training/game-controllers/controller-input + + int type = AInputEvent_getType(event); + int source = AInputEvent_getSource(event); + + if (type == AINPUT_EVENT_TYPE_MOTION) + { + if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || + ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) + { + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; + + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_X, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Y, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Z, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_RZ, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f; + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f; + + // dpad is reported as an axis on android + float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); + float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0); + + if (dpadX == 1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; + } + else if (dpadX == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; + } + + if (dpadY == 1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + else if (dpadY == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + + return 1; // Handled gamepad axis motion + } + } + else if (type == AINPUT_EVENT_TYPE_KEY) + { + int32_t keycode = AKeyEvent_getKeyCode(event); + //int32_t AKeyEvent_getMetaState(event); + + // Handle gamepad button presses and releases + if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || + ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) + { + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; + + GamepadButton button = AndroidTranslateGamepadButton(keycode); + + if (button == GAMEPAD_BUTTON_UNKNOWN) return 1; + + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + CORE.Input.Gamepad.currentButtonState[0][button] = 1; + } + else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up + + return 1; // Handled gamepad button + } + + // Save current button and its state + // NOTE: Android key action is 0 for down and 1 for up + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[keycode] = 1; + else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up + + if (keycode == AKEYCODE_POWER) + { + // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS + // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS + // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected. + // NOTE: AndroidManifest.xml must have + // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour + return 0; + } + else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU)) + { + // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS! + return 1; + } + else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN)) + { + // Set default OS behaviour + return 0; + } + + return 0; + } + + // Register touch points count + CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event); + + for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + // Register touch points id + CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i); + + // Register touch points position + CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; + + // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height + float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x) / (float)CORE.Window.display.width; + float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y) / (float)CORE.Window.display.height; + CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x * widthRatio - (float)CORE.Window.renderOffset.x / 2; + CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y * heightRatio - (float)CORE.Window.renderOffset.y / 2; + } + + int32_t action = AMotionEvent_getAction(event); + unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; + +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID + GestureEvent gestureEvent = { 0 }; + + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + + // Register touch actions + if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP; + else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; + else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; + + for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + } + + // Gesture data is sent to gestures system for processing + ProcessGestureEvent(gestureEvent); +#endif + + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + + if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP) + { + // One of the touchpoints is released, remove it from touch point arrays + for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++) + { + CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; + CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; + } + + CORE.Input.Touch.pointCount--; + } + + // When all touchpoints are tapped and released really quickly, this event is generated + if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; + + if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; + else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; + + return 0; +} + +// EOF diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c new file mode 100644 index 000000000..f8c6b0e2c --- /dev/null +++ b/src/rcore_desktop.c @@ -0,0 +1,2074 @@ +/********************************************************************************************** +* +* rcore_desktop - Functions to manage window, graphics device and inputs +* +* PLATFORM: DESKTOP +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - OSX/macOS +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_DESKTOP_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_DESKTOP -not used- +* +* DEPENDENCIES: +* rglfw - Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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. +* +**********************************************************************************************/ + +#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) +#include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management + // NOTE: GLFW3 already includes gl.h (OpenGL) headers + +// Support retrieving native window handlers +#if defined(_WIN32) + typedef void *PVOID; + typedef PVOID HANDLE; + typedef HANDLE HWND; + #define GLFW_EXPOSE_NATIVE_WIN32 + #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h + #include "GLFW/glfw3native.h" + + #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + // NOTE: Those functions require linking with winmm library + unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); + unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); + #endif +#endif +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) + #include // Required for: timespec, nanosleep(), select() - POSIX + + //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type + //#define GLFW_EXPOSE_NATIVE_WAYLAND + //#define GLFW_EXPOSE_NATIVE_MIR + #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window() +#endif +#if defined(__APPLE__) + #include // Required for: usleep() + + //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition + void *glfwGetCocoaWindow(GLFWwindow* handle); + #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +// Error callback event +static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error + +// Window callbacks events +static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized +static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window + +// Input callbacks events +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed +static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel +static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE structure to 0 + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + // Initialize graphics device (display device and OpenGL context) + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + #if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif +#else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes + #endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + + glfwDestroyWindow(CORE.Window.handle); + glfwTerminate(); + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +// NOTE: By default, if KEY_ESCAPE pressed or window close icon clicked +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) + { + // While window minimized, stop loop execution + while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); + + CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle); + + // Reset close status for next frame + glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE); + + return CORE.Window.shouldClose; + } + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return CORE.Window.resizedLastFrame; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + if (!CORE.Window.fullscreen) + { + // Store previous window position (in case we exit fullscreen) + glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); + + int monitorCount = 0; + int monitorIndex = GetCurrentMonitor(); + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + // Use current monitor, so we correctly get the display the window is on + GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL; + + if (monitor == NULL) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); + + CORE.Window.fullscreen = false; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + + glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + else + { + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + + } + else + { + CORE.Window.fullscreen = false; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + + glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // NOTE: V-Sync can be enabled by graphic driver configuration + if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + glfwMaximizeWindow(CORE.Window.handle); + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; + } +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + // NOTE: Following function launches callback that sets appropriate flag! + glfwIconifyWindow(CORE.Window.handle); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + // Restores the specified window if it was previously iconified (minimized) or maximized + glfwRestoreWindow(CORE.Window.handle); + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + } +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it + bool wasOnFullscreen = false; + if (CORE.Window.fullscreen) + { + CORE.Window.previousPosition = CORE.Window.position; + ToggleFullscreen(); + wasOnFullscreen = true; + } + + const int monitor = GetCurrentMonitor(); + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + + if (mode) + { + if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) + { + // Store screen position and size + // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here + if (!wasOnFullscreen) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + CORE.Window.previousScreen = CORE.Window.screen; + + // Set undecorated and topmost modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + + // Get monitor position and size + int monitorPosX = 0; + int monitorPosY = 0; + glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY); + const int monitorWidth = mode->width; + const int monitorHeight = mode->height; + + // Set screen position and size + glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); + glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + // Remove topmost and undecorated modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + + // Return previous screen size and position + // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly + glfwSetWindowSize(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + // Check previous state and requested state to apply required changes + // NOTE: In most cases the functions already change the flags internally + + // State change: FLAG_VSYNC_HINT + if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0)) + { + glfwSwapInterval(1); + CORE.Window.flags |= FLAG_VSYNC_HINT; + } + + // State change: FLAG_BORDERLESS_WINDOWED_MODE + // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running + if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) + { + ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_FULLSCREEN_MODE + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE)) + { + ToggleFullscreen(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_RESIZABLE + if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_RESIZABLE; + } + + // State change: FLAG_WINDOW_UNDECORATED + if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + } + + // State change: FLAG_WINDOW_HIDDEN + if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) + { + glfwHideWindow(CORE.Window.handle); + CORE.Window.flags |= FLAG_WINDOW_HIDDEN; + } + + // State change: FLAG_WINDOW_MINIMIZED + if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) + { + //GLFW_ICONIFIED + MinimizeWindow(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_MAXIMIZED + if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) + { + //GLFW_MAXIMIZED + MaximizeWindow(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_UNFOCUSED + if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; + } + + // State change: FLAG_WINDOW_TOPMOST + if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + } + + // State change: FLAG_WINDOW_ALWAYS_RUN + if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) + { + CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN; + } + + // The following states can not be changed after window creation + + // State change: FLAG_WINDOW_TRANSPARENT + if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); + } + + // State change: FLAG_WINDOW_HIGHDPI + if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); + } + + // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH + if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH; + } + + // State change: FLAG_MSAA_4X_HINT + if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); + } + + // State change: FLAG_INTERLACED_HINT + if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0)) + { + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); + } +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + // Check previous state and requested state to apply required changes + // NOTE: In most cases the functions already change the flags internally + + // State change: FLAG_VSYNC_HINT + if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0)) + { + glfwSwapInterval(0); + CORE.Window.flags &= ~FLAG_VSYNC_HINT; + } + + // State change: FLAG_BORDERLESS_WINDOWED_MODE + // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running + if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) + { + ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_FULLSCREEN_MODE + if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0)) + { + ToggleFullscreen(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_RESIZABLE + if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; + } + + // State change: FLAG_WINDOW_HIDDEN + if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) + { + glfwShowWindow(CORE.Window.handle); + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; + } + + // State change: FLAG_WINDOW_MINIMIZED + if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) + { + RestoreWindow(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_MAXIMIZED + if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) + { + RestoreWindow(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_UNDECORATED + if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + } + + // State change: FLAG_WINDOW_UNFOCUSED + if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; + } + + // State change: FLAG_WINDOW_TOPMOST + if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + } + + // State change: FLAG_WINDOW_ALWAYS_RUN + if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) + { + CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN; + } + + // The following states can not be changed after window creation + + // State change: FLAG_WINDOW_TRANSPARENT + if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); + } + + // State change: FLAG_WINDOW_HIGHDPI + if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); + } + + // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH + if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH; + } + + // State change: FLAG_MSAA_4X_HINT + if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); + } + + // State change: FLAG_INTERLACED_HINT + if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0)) + { + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); + } +} + +// Set icon for window +// NOTE 1: Image must be in RGBA format, 8bit per channel +// NOTE 2: Image is scaled by the OS for all required sizes +void SetWindowIcon(Image image) +{ + if (image.data == NULL) + { + // Revert to the default window icon, pass in an empty image array + glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + } + else + { + if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + GLFWimage icon[1] = { 0 }; + + icon[0].width = image.width; + icon[0].height = image.height; + icon[0].pixels = (unsigned char *)image.data; + + // NOTE 1: We only support one image icon + // NOTE 2: The specified image data is copied before this function returns + glfwSetWindowIcon(CORE.Window.handle, 1, icon); + } + else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); + } +} + +// Set icon for window, multiple images +// NOTE 1: Images must be in RGBA format, 8bit per channel +// NOTE 2: The multiple images are used depending on provided sizes +// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 +void SetWindowIcons(Image *images, int count) +{ + if ((images == NULL) || (count <= 0)) + { + // Revert to the default window icon, pass in an empty image array + glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + } + else + { + int valid = 0; + GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); + + for (int i = 0; i < count; i++) + { + if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + icons[valid].width = images[i].width; + icons[valid].height = images[i].height; + icons[valid].pixels = (unsigned char *)images[i].data; + + valid++; + } + else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); + } + // NOTE: Images data is copied internally before this function returns + glfwSetWindowIcon(CORE.Window.handle, valid, icons); + + RL_FREE(icons); + } +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; + glfwSetWindowTitle(CORE.Window.handle, title); +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + glfwSetWindowPos(CORE.Window.handle, x, y); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + if (CORE.Window.fullscreen) + { + TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + } + else + { + TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + + const int screenWidth = CORE.Window.screen.width; + const int screenHeight = CORE.Window.screen.height; + int monitorWorkareaX = 0; + int monitorWorkareaY = 0; + int monitorWorkareaWidth = 0; + int monitorWorkareaHeight = 0; + glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); + + // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it + if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); + else + { + const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); + const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); + glfwSetWindowPos(CORE.Window.handle, x, y); + } + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + glfwSetWindowSize(CORE.Window.handle, width, height); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + if (opacity >= 1.0f) opacity = 1.0f; + else if (opacity <= 0.0f) opacity = 0.0f; + glfwSetWindowOpacity(CORE.Window.handle, opacity); +} + +// Set window focused +void SetWindowFocused(void) +{ + glfwFocusWindow(CORE.Window.handle); +} + +// Get native window handle +void *GetWindowHandle(void) +{ +#if defined(_WIN32) + // NOTE: Returned handle is: void *HWND (windows.h) + return glfwGetWin32Window(CORE.Window.handle); +#endif +#if defined(__linux__) + // NOTE: Returned handle is: unsigned long Window (X.h) + // typedef unsigned long XID; + // typedef XID Window; + //unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle); + //return NULL; // TODO: Find a way to return value... cast to void *? + return (void *)CORE.Window.handle; +#endif +#if defined(__APPLE__) + // NOTE: Returned handle is: (objc_object *) + return (void *)glfwGetCocoaWindow(CORE.Window.handle); +#endif + + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + int monitorCount = 0; + + glfwGetMonitors(&monitorCount); + + return monitorCount; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + int index = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + GLFWmonitor *monitor = NULL; + + if (monitorCount >= 1) + { + if (IsWindowFullscreen()) + { + // Get the handle of the monitor that the specified window is in full screen on + monitor = glfwGetWindowMonitor(CORE.Window.handle); + + for (int i = 0; i < monitorCount; i++) + { + if (monitors[i] == monitor) + { + index = i; + break; + } + } + } + else + { + int x = 0; + int y = 0; + + glfwGetWindowPos(CORE.Window.handle, &x, &y); + + for (int i = 0; i < monitorCount; i++) + { + int mx = 0; + int my = 0; + + monitor = monitors[i]; + glfwGetMonitorPos(monitor, &mx, &my); + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + + if (mode) + { + const int width = mode->width; + const int height = mode->height; + + if ((x >= mx) && + (x < (mx + width)) && + (y >= my) && + (y < (my + height))) + { + index = i; + break; + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + } + } + + return index; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + int x, y; + glfwGetMonitorPos(monitors[monitor], &x, &y); + + return (Vector2){ (float)x, (float)y }; + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + int width = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + + if (mode) width = mode->width; + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return width; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + int height = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + + if (mode) height = mode->height; + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return height; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + int width = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], &width, NULL); + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return width; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + int height = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &height); + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return height; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + int refresh = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]); + refresh = vidmode->refreshRate; + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return refresh; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + return glfwGetMonitorName(monitors[monitor]); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + int x = 0; + int y = 0; + + glfwGetWindowPos(CORE.Window.handle, &x, &y); + + return (Vector2){ (float)x, (float)y }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + float xdpi = 1.0; + float ydpi = 1.0; + Vector2 scale = { 1.0f, 1.0f }; + Vector2 windowPos = GetWindowPosition(); + + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + // Check window monitor + for (int i = 0; i < monitorCount; i++) + { + glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi); + + int xpos, ypos, width, height; + glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height); + + if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) && + (windowPos.y >= ypos) && (windowPos.y < ypos + height)) + { + scale.x = xdpi; + scale.y = ydpi; + break; + } + } + + return scale; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + glfwSetClipboardString(CORE.Window.handle, text); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + return glfwGetClipboardString(CORE.Window.handle); +} + +// Show mouse cursor +void ShowCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + CORE.Input.Mouse.cursorHidden = false; +} + +// Hides mouse cursor +void HideCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + CORE.Input.Mouse.cursorHidden = true; +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + // 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) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = glfwGetTime(); // Elapsed time since glfwInit() + return time; +} + +// Takes a screenshot of current screen (saved a .png) +// WARNING: This function requires [rtextures] module functionality +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char)); +#if defined(_WIN32) + sprintf(cmd, "explorer \"%s\"", url); +#endif +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) + sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser +#endif +#if defined(__APPLE__) + sprintf(cmd, "open '%s'", url); +#endif + int result = system(cmd); + if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created"); + RL_FREE(cmd); + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +// NOTE: default exitKey is ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + const char *name = NULL; + + if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); + + return name; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + return glfwUpdateGamepadMappings(mappings); +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + Vector2 position = { 0 }; + + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; +} + +// 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; + + // NOTE: emscripten not implemented + glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + float result = 0.0f; + + if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; + else result = (float)CORE.Input.Mouse.currentWheelMove.y; + + return result; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + CORE.Input.Mouse.cursor = cursor; + if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL); + else + { + // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values + glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor)); + } +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return GetMouseX(); +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return GetMouseY(); +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + // TODO: GLFW does not support multi-touch input just yet + // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch + // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages + if (index == 0) position = GetMousePosition(); + + return position; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(CORE.Window.handle); +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) + + // Register previous keys states + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Register previous mouse states + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + + // Register previous mouse wheel state + CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; + CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; + + // Register previous mouse position + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Check if gamepads are ready + // NOTE: We do it here in case of disconnection + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; + else CORE.Input.Gamepad.ready[i] = false; + } + + // Register gamepads buttons events + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available + { + // Register previous gamepad states + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; + + // Get current gamepad state + // NOTE: There is no callback available, so we get it manually + GLFWgamepadstate state = { 0 }; + glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller + + const unsigned char *buttons = state.buttons; + + for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) + { + int button = -1; // GamepadButton enum values assigned + + switch (k) + { + case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + + case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + + case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break; + case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + + case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + + case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; + default: break; + } + + if (button != -1) // Check for valid button + { + if (buttons[k] == GLFW_PRESS) + { + CORE.Input.Gamepad.currentButtonState[i][button] = 1; + CORE.Input.Gamepad.lastButtonPressed = button; + } + else CORE.Input.Gamepad.currentButtonState[i][button] = 0; + } + } + + // Get current axis state + const float *axes = state.axes; + + for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) + { + CORE.Input.Gamepad.axisState[i][k] = axes[k]; + } + + // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis) + CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f); + CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f); + + CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1; + } + } + + CORE.Window.resizedLastFrame = false; + + if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) + else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; + + glfwInitAllocator(&allocator); +*/ +#if defined(__APPLE__) + glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); +#endif + + if (!glfwInit()) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); + return false; + } + + glfwDefaultWindowHints(); // Set default windows hints + //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits + //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits + //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits + //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits + //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits + //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window + //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API + //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers + + // Check window creation flags + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true; + + if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window + else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden + + if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window + else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window + + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window + else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable + + // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + + // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); + else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); + + if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); + else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); + + // NOTE: Some GLFW flags are not supported on HTML5 + if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer + else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Resize window content area based on the monitor content scale. + // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11. + // On platforms like macOS the resolution of the framebuffer is changed independently of the window size. + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on +#if defined(__APPLE__) + glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); +#endif + } + else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); + + // Mouse passthrough + if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); + else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); + + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 + } + + // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version + // with backward compatibility to older OpenGL versions. + // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context. + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) + } + else if (rlGetVersion() == RL_OPENGL_33) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! + // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE +#if defined(__APPLE__) + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility +#else + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! +#endif + //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context + } + else if (rlGetVersion() == RL_OPENGL_43) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + } + + // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions. + // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn. + // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience. + // REF: https://github.com/raysan5/raylib/issues/1554 + if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); + + // Find monitor resolution + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); + if (!monitor) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); + return false; + } + + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + + CORE.Window.display.width = mode->width; + CORE.Window.display.height = mode->height; + + // Set screen width/height to the display width/height if they are 0 + if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; + if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; + + if (CORE.Window.fullscreen) + { + // remember center for switchinging from fullscreen to window + if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) + { + // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. + // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11. + CORE.Window.position.x = CORE.Window.display.width/4; + CORE.Window.position.y = CORE.Window.display.height/4; + } + else + { + CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2; + CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2; + } + + if (CORE.Window.position.x < 0) CORE.Window.position.x = 0; + if (CORE.Window.position.y < 0) CORE.Window.position.y = 0; + + // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor + int count = 0; + const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); + + // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height + for (int i = 0; i < count; i++) + { + if ((unsigned int)modes[i].width >= CORE.Window.screen.width) + { + if ((unsigned int)modes[i].height >= CORE.Window.screen.height) + { + CORE.Window.display.width = modes[i].width; + CORE.Window.display.height = modes[i].height; + break; + } + } + } + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + + // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, + // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), + // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched + // by the sides to fit all monitor space... + + // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight + // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset) + // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale + // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed... + // HighDPI monitors are properly considered in a following similar function: SetupViewport() + SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + + CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + + // NOTE: Full-screen change, not working properly... + //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + else + { + // If we are windowed fullscreen, ensures that window does not minimize when focus is lost + if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) + { + glfwWindowHint(GLFW_AUTO_ICONIFY, 0); + } + + // No-fullscreen window creation + CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + + if (CORE.Window.handle) + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + } + } + + if (!CORE.Window.handle) + { + glfwTerminate(); + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); + return false; + } + + // Set window callback events + glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback); + glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); + glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + + // Set input callback events + glfwSetKeyCallback(CORE.Window.handle, KeyCallback); + glfwSetCharCallback(CORE.Window.handle, CharCallback); + glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); + glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + + glfwMakeContextCurrent(CORE.Window.handle); + + glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + + glfwSwapInterval(0); // No V-Sync by default + + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need + // to be activated on web platforms since VSync is enforced there. + if (CORE.Window.flags & FLAG_VSYNC_HINT) + { + // WARNING: It seems to hit a critical render path in Intel HD Graphics + glfwSwapInterval(1); + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); + } + + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. + // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); +#if !defined(__APPLE__) + glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight); + + // Screen scaling matrix is required in case desired screen area is different from display area + CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); + + // Mouse input scaling for the new screen size + SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); +#endif + } + + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + CORE.Window.currentFbo.width = fbWidth; + CORE.Window.currentFbo.height = fbHeight; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + + rlLoadExtensions(glfwGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// GLFW3 Error Callback, runs on GLFW3 error +static void ErrorCallback(int error, const char *description) +{ + TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description); +} + +// GLFW3 WindowSize Callback, runs when window is resizedLastFrame +// NOTE: Window resizing not allowed by default +static void WindowSizeCallback(GLFWwindow *window, int width, int height) +{ + // Reset viewport and projection matrix for new size + SetupViewport(width, height); + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return; + + // Set current screen size +#if defined(__APPLE__) + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; +#else + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + Vector2 windowScaleDPI = GetWindowScaleDPI(); + + CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x); + CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y); + } + else + { + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + } +#endif + + // NOTE: Postprocessing texture is not scaled to new size +} + +// GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowIconifyCallback(GLFWwindow *window, int iconified) +{ + if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified + else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored +} + +// GLFW3 WindowMaximize Callback, runs when window is maximized/restored +static void WindowMaximizeCallback(GLFWwindow *window, int maximized) +{ + if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized + else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored +} + +// GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowFocusCallback(GLFWwindow *window, int focused) +{ + if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused + else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus +} + +// GLFW3 Window Drop Callback, runs when drop files into window +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) +{ + if (count > 0) + { + // In case previous dropped filepaths have not been freed, we free them + if (CORE.Window.dropFileCount > 0) + { + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); + + RL_FREE(CORE.Window.dropFilepaths); + + CORE.Window.dropFileCount = 0; + CORE.Window.dropFilepaths = NULL; + } + + // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy + CORE.Window.dropFileCount = count; + CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); + + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) + { + CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[i], paths[i]); + } + } +} + +// GLFW3 Keyboard Callback, runs on key pressed +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) +{ + if (key < 0) return; // Security check, macOS fn key generates -1 + + // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 + // to work properly with our implementation (IsKeyDown/IsKeyUp checks) + if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; + else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; + else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; + + // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys + if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) || + ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1; + + // Check if there is space available in the key queue + if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) + { + // Add character to the queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + // Check the exit key to set close window + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + +#if defined(SUPPORT_SCREEN_CAPTURE) + if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) + { +#if defined(SUPPORT_GIF_RECORDING) + if (mods & GLFW_MOD_CONTROL) + { + if (gifRecording) + { + gifRecording = false; + + MsfGifResult result = msf_gif_end(&gifState); + + SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); + msf_gif_free(result); + + TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); + } + else + { + gifRecording = true; + gifFrameCounter = 0; + + Vector2 scale = GetWindowScaleDPI(); + msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + screenshotCounter++; + + TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); + } + } + else +#endif // SUPPORT_GIF_RECORDING + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + } +#endif // SUPPORT_SCREEN_CAPTURE + +#if defined(SUPPORT_EVENTS_AUTOMATION) + if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) + { + eventsRecording = !eventsRecording; + + // On finish recording, we export events into a file + if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); + } + else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) + { + LoadAutomationEvents("eventsrec.rep"); + eventsPlaying = true; + + TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); + } +#endif +} + +// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) +static void CharCallback(GLFWwindow *window, unsigned int key) +{ + //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key); + + // NOTE: Registers any key down considering OS keyboard layout but + // does not detect action events, those should be managed by user... + // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907 + // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char + + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; + CORE.Input.Keyboard.charPressedQueueCount++; + } +} + +// GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) +{ + // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now, + // but future releases may add more actions (i.e. GLFW_REPEAT) + CORE.Input.Mouse.currentButtonState[button] = action; + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + // Register touch actions + if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP; + + // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback() + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = GetMousePosition(); + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); + +#endif +} + +// GLFW3 Cursor Position Callback, runs on mouse move +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) +{ + CORE.Input.Mouse.currentPosition.x = (float)x; + CORE.Input.Mouse.currentPosition.y = (float)y; + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + gestureEvent.touchAction = TOUCH_ACTION_MOVE; + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = CORE.Input.Touch.position[0]; + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); +#endif +} + +// GLFW3 Scrolling Callback, runs on mouse wheel +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) +{ + CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; +} + +// GLFW3 CursorEnter Callback, when cursor enters the window +static void CursorEnterCallback(GLFWwindow *window, int enter) +{ + if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + else CORE.Input.Mouse.cursorOnScreen = false; +} + +// EOF + diff --git a/src/rcore_drm.c b/src/rcore_drm.c new file mode 100644 index 000000000..2c05ab46e --- /dev/null +++ b/src/rcore_drm.c @@ -0,0 +1,2059 @@ +/********************************************************************************************** +* +* rcore_drm - Functions to manage window, graphics device and inputs +* +* PLATFORM: DRM +* - Raspberry Pi 0-5 +* - Linux native mode (KMS driver) +* +* LIMITATIONS: +* - Most of the window/monitor functions are not implemented (not required) +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_DRM_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_DRM -not used- +* +* DEPENDENCIES: +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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 "rcore.h" + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +static void InitKeyboard(void); // Initialize raw keyboard system +static void RestoreKeyboard(void); // Restore keyboard system +#if defined(SUPPORT_SSH_KEYBOARD_RPI) +static void ProcessKeyboard(void); // Process keyboard events +#endif + +static void InitEvdevInput(void); // Initialize evdev inputs +static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate +static void PollKeyboardEvents(void); // Process evdev keyboard events +static void *EventThread(void *arg); // Input device events reading thread + +static void InitGamepad(void); // Initialize raw gamepad input +static void *GamepadThread(void *arg); // Mouse reading thread + +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 + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f}; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + // Initialize graphics device (display device and OpenGL context) + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } + else + SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); +#if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 2, rec.y + 2, 1, 1}); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2}); + } +#endif +#else +#if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = {rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8}; + SetShapesTexture(texture, (Rectangle){0.0f, 0.0f, 1.0f, 1.0f}); // WARNING: Module required: rshapes +#endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + + 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; + } + + // Close surface, context and display + if (CORE.Window.device != EGL_NO_DISPLAY) + { + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + if (CORE.Window.context != EGL_NO_CONTEXT) + { + eglDestroyContext(CORE.Window.device, CORE.Window.context); + CORE.Window.context = EGL_NO_CONTEXT; + } + + eglTerminate(CORE.Window.device); + CORE.Window.device = EGL_NO_DISPLAY; + } + + // 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 + + CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called + + // Close the evdev keyboard + if (CORE.Input.Keyboard.fd != -1) + { + close(CORE.Input.Keyboard.fd); + CORE.Input.Keyboard.fd = -1; + } + + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].threadId) + { + pthread_join(CORE.Input.eventWorker[i].threadId, NULL); + } + } + + if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +// NOTE: By default, if KEY_ESCAPE pressed +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return true; +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_DRM"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_DRM"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_DRM"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_DRM"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_DRM"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_DRM"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_DRM"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_DRM"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_DRM"); +} + +// 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 PLATFORM_DRM"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_DRM"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_DRM"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_DRM"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_DRM"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_DRM"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_DRM"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_DRM"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + int refresh = 0; + + if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0)) + { + refresh = CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh; + } + + return refresh; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_DRM"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_DRM"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_DRM"); + return NULL; +} + +// 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; +} + +// 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; +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_WEB + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + TRACELOG(LOG_WARNING, "OpenURL() not implemented on PLATFORM_DRM"); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +// NOTE: default exitKey is ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + const char *name = NULL; + + if (CORE.Input.Gamepad.ready[gamepad]) + { + ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); + name = CORE.Input.Gamepad.name[gamepad]; + } + + return name; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + int axisCount = 0; + if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); + CORE.Input.Gamepad.axisCount = axisCount; + + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + Vector2 position = { 0 }; + + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; +} + +// 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; +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + float result = 0.0f; + + if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; + else result = (float)CORE.Input.Mouse.currentWheelMove.y; + + return result; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_DRM"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return GetMouseX(); +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return GetMouseY(); +} + +// Get touch position XY for a touch point index (relative to screen size) +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + 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); + + return position; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(CORE.Window.device, CORE.Window.surface); + + if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + + struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface); + if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); + + 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 (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); + + 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 (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); + + if (CORE.Window.prevFB) + { + result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); + } + + CORE.Window.prevFB = fb; + + if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); + + CORE.Window.prevBO = bo; +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + + // Register previous keys states + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + PollKeyboardEvents(); + + // Register previous mouse states + CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; + CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; + CORE.Input.Mouse.eventWheelMove = (Vector2){ 0.0f, 0.0f }; + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) + { + CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i]; + } + + // Register gamepads buttons events + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (CORE.Input.Gamepad.ready[i]) + { + // Register previous gamepad states + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; + } + } + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + +#if defined(SUPPORT_SSH_KEYBOARD_RPI) + // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. + // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console + + if (!CORE.Input.Keyboard.evtMode) ProcessKeyboard(); + + // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread() + // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() +#endif +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the window minimum and maximum default values to 0 + CORE.Window.windowMin.width = 0; + CORE.Window.windowMin.height = 0; + CORE.Window.windowMax.width = 0; + CORE.Window.windowMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + 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 platform-gpu-card"); + CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) + + if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + { + TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); + CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded + } + + if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + { + 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 (CORE.Window.fd == -1) + { + 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: Connectors found: %i", 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: Connector modes detected: %i", con->count_modes); + + if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); + CORE.Window.connector = con; + break; + } + else + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); + drmModeFreeConnector(con); + } + } + + if (!CORE.Window.connector) + { + TRACELOG(LOG_WARNING, "DISPLAY: 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, "DISPLAY: 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, "DISPLAY: 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, "DISPLAY: 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, "DISPLAY: 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, "DISPLAY: Failed to find a suitable DRM connector mode"); + 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, "DISPLAY: Selected DRM connector 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, "DISPLAY: 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, "DISPLAY: Failed to create GBM surface"); + return false; + } + + EGLint samples = 0; + EGLint sampleBuffer = 0; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + samples = 4; + sampleBuffer = 1; + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + } + + const EGLint framebufferAttribs[] = + { + EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android! + 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) + //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 + EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA + EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) + EGL_NONE + }; + + const EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint numConfigs = 0; + + // Get an EGL device connection + CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice); + if (CORE.Window.device == EGL_NO_DISPLAY) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Initialize the EGL device connection + if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) + { + // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs)) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); + return false; + } + + TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs); + + EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs)); + if (!configs) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs"); + return false; + } + + EGLint matchingNumConfigs = 0; + 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; + } + + TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs); + + // 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; + } + } + + RL_FREE(configs); + + if (!found) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config"); + return false; + } + + // Set rendering API + eglBindAPI(EGL_OPENGL_ES_API); + + // Create an EGL rendering context + CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); + if (CORE.Window.context == EGL_NO_CONTEXT) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); + return false; + } + + // Create an EGL window surface + //--------------------------------------------------------------------------------- + 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); + + // There must be at least one frame displayed before the buffers are swapped + //eglSwapInterval(CORE.Window.device, 1); + + if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + return false; + } + else + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// Initialize Keyboard system (using standard input) +static void InitKeyboard(void) +{ + // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor, + // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE + + // Save terminal keyboard settings + tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings); + + // Reconfigure terminal with new settings + struct termios keyboardNewSettings = { 0 }; + keyboardNewSettings = CORE.Input.Keyboard.defaultSettings; + + // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing + // NOTE: ISIG controls if ^C and ^Z generate break signals or not + keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG); + //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF); + keyboardNewSettings.c_cc[VMIN] = 1; + keyboardNewSettings.c_cc[VTIME] = 0; + + // Set new keyboard settings (change occurs immediately) + tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings); + + // Save old keyboard mode to restore it at the end + CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags + fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified + + // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno) + int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode); + + // In case of failure, it could mean a remote keyboard is used (SSH) + if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used"); + else + { + // Reconfigure keyboard mode to get: + // - scancodes (K_RAW) + // - keycodes (K_MEDIUMRAW) + // - ASCII chars (K_XLATE) + // - UNICODE chars (K_UNICODE) + ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); // ASCII chars + } + + // Register keyboard restore when program finishes + atexit(RestoreKeyboard); +} + +// Restore default keyboard input +static void RestoreKeyboard(void) +{ + // Reset to default keyboard settings + tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings); + + // Reconfigure keyboard to default mode + fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags); + ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode); +} + +#if defined(SUPPORT_SSH_KEYBOARD_RPI) +// Process keyboard inputs +static void ProcessKeyboard(void) +{ + #define MAX_KEYBUFFER_SIZE 32 // Max size in bytes to read + + // Keyboard input polling (fill keys[256] array with status) + int bufferByteCount = 0; // Bytes available on the buffer + char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 }; // Max keys to be read at a time + + // Read availables keycodes from stdin + bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE); // POSIX system call + + // Reset pressed keys array (it will be filled below) + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.currentKeyState[i] = 0; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Fill all read bytes (looking for keys) + for (int i = 0; i < bufferByteCount; i++) + { + // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code! + // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42 + if (keysBuffer[i] == 0x1b) + { + // Check if ESCAPE key has been pressed to stop program + if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1; + else + { + if (keysBuffer[i + 1] == 0x5b) // Special function key + { + if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) + { + // Process special function keys (F1 - F12) + switch (keysBuffer[i + 3]) + { + case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break; // raylib KEY_F1 + case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break; // raylib KEY_F2 + case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break; // raylib KEY_F3 + case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break; // raylib KEY_F4 + case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break; // raylib KEY_F5 + case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break; // raylib KEY_F6 + case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break; // raylib KEY_F7 + case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break; // raylib KEY_F8 + case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break; // raylib KEY_F9 + case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break; // raylib KEY_F10 + case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break; // raylib KEY_F11 + case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break; // raylib KEY_F12 + default: break; + } + + if (keysBuffer[i + 2] == 0x5b) i += 4; + else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5; + } + else + { + switch (keysBuffer[i + 2]) + { + case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break; // raylib KEY_UP + case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break; // raylib KEY_DOWN + case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break; // raylib KEY_RIGHT + case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break; // raylib KEY_LEFT + default: break; + } + + i += 3; // Jump to next key + } + + // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT) + } + } + } + else if (keysBuffer[i] == 0x0a) // raylib KEY_ENTER (don't mix with KEY_*) + { + CORE.Input.Keyboard.currentKeyState[257] = 1; + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue + CORE.Input.Keyboard.keyPressedQueueCount++; + } + else if (keysBuffer[i] == 0x7f) // raylib KEY_BACKSPACE + { + CORE.Input.Keyboard.currentKeyState[259] = 1; + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue + CORE.Input.Keyboard.keyPressedQueueCount++; + } + else + { + // Translate lowercase a-z letters to A-Z + if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122)) + { + CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1; + } + else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1; + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i]; // Add keys pressed into queue + CORE.Input.Keyboard.keyPressedQueueCount++; + } + } + + // Check exit key (same functionality as GLFW3 KeyCallback()) + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; + +#if defined(SUPPORT_SCREEN_CAPTURE) + // Check screen capture key (raylib key: KEY_F12) + if (CORE.Input.Keyboard.currentKeyState[301] == 1) + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } +#endif +} +#endif // SUPPORT_SSH_KEYBOARD_RPI + +// Initialise user input from evdev(/dev/input/event) +// this means mouse, keyboard or gamepad devices +static void InitEvdevInput(void) +{ + char path[MAX_FILEPATH_LENGTH] = { 0 }; + DIR *directory = NULL; + struct dirent *entity = NULL; + + // Initialise keyboard file descriptor + CORE.Input.Keyboard.fd = -1; + + // Reset variables + for (int i = 0; i < MAX_TOUCH_POINTS; ++i) + { + CORE.Input.Touch.position[i].x = -1; + CORE.Input.Touch.position[i].y = -1; + } + + // Reset keyboard key state + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.currentKeyState[i] = 0; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Open the linux directory of "/dev/input" + directory = opendir(DEFAULT_EVDEV_PATH); + + if (directory) + { + while ((entity = readdir(directory)) != NULL) + { + if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*" + (strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*" + { + sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); + ConfigureEvdevDevice(path); // Configure the device if appropriate + } + } + + closedir(directory); + } + else TRACELOG(LOG_WARNING, "RPI: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH); +} + +// Identifies a input device and configures it for use if appropriate +static void ConfigureEvdevDevice(char *device) +{ + #define BITS_PER_LONG (8*sizeof(long)) + #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1) + #define OFF(x) ((x)%BITS_PER_LONG) + #define BIT(x) (1UL<> OFF(bit)) & 1) + + struct input_absinfo absinfo = { 0 }; + unsigned long evBits[NBITS(EV_MAX)] = { 0 }; + unsigned long absBits[NBITS(ABS_MAX)] = { 0 }; + unsigned long relBits[NBITS(REL_MAX)] = { 0 }; + unsigned long keyBits[NBITS(KEY_MAX)] = { 0 }; + bool hasAbs = false; + bool hasRel = false; + bool hasAbsMulti = false; + int freeWorkerId = -1; + int fd = -1; + + InputEventWorker *worker = NULL; + + // Open the device and allocate worker + //------------------------------------------------------------------------------------------------------- + // Find a free spot in the workers array + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].threadId == 0) + { + freeWorkerId = i; + break; + } + } + + // Select the free worker from array + if (freeWorkerId >= 0) + { + worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker + memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker + } + else + { + TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device); + return; + } + + // Open the device + fd = open(device, O_RDONLY | O_NONBLOCK); + if (fd < 0) + { + TRACELOG(LOG_WARNING, "RPI: Failed to open input device: %s", device); + return; + } + worker->fd = fd; + + // Grab number on the end of the devices name "event" + int devNum = 0; + char *ptrDevName = strrchr(device, 't'); + worker->eventNum = -1; + + if (ptrDevName != NULL) + { + if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum; + } + else worker->eventNum = 0; // TODO: HACK: Grab number for mouse0 device! + + // At this point we have a connection to the device, but we don't yet know what the device is. + // It could be many things, even as simple as a power button... + //------------------------------------------------------------------------------------------------------- + + // Identify the device + //------------------------------------------------------------------------------------------------------- + ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties + + // Check for absolute input devices + if (TEST_BIT(evBits, EV_ABS)) + { + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); + + // Check for absolute movement support (usually touchscreens, but also joysticks) + if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) + { + hasAbs = true; + + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + worker->absRange.x = absinfo.minimum; + worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + worker->absRange.y = absinfo.minimum; + worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + + // Check for multiple absolute movement support (usually multitouch touchscreens) + if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) + { + hasAbsMulti = true; + + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + worker->absRange.x = absinfo.minimum; + worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + worker->absRange.y = absinfo.minimum; + worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + } + + // Check for relative movement support (usually mouse) + if (TEST_BIT(evBits, EV_REL)) + { + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); + + if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; + } + + // Check for button support to determine the device type(usually on all input devices) + if (TEST_BIT(evBits, EV_KEY)) + { + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); + + if (hasAbs || hasAbsMulti) + { + if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen + if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet + if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet + if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet + if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device + } + + if (hasRel) + { + if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse + if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse + } + + if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad + + if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard + } + //------------------------------------------------------------------------------------------------------- + + // Decide what to do with the device + //------------------------------------------------------------------------------------------------------- + if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1)) + { + // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a + // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate + // threads so that they don't drop events when the frame rate is slow. + TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); + CORE.Input.Keyboard.fd = worker->fd; + } + else if (worker->isTouch || worker->isMouse) + { + // Looks like an interesting device + TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device, + worker->isMouse? "mouse " : "", + worker->isMultitouch? "multitouch " : "", + worker->isTouch? "touchscreen " : "", + worker->isGamepad? "gamepad " : ""); + + // Create a thread for this device + int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker); + if (error != 0) + { + TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error); + worker->threadId = 0; + close(fd); + } + +#if defined(USE_LAST_TOUCH_DEVICE) + // Find touchscreen with the highest index + int maxTouchNumber = -1; + + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; + } + + // Find touchscreens with lower indexes + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) + { + if (CORE.Input.eventWorker[i].threadId != 0) + { + TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i); + pthread_cancel(CORE.Input.eventWorker[i].threadId); + close(CORE.Input.eventWorker[i].fd); + } + } + } +#endif + } + else close(fd); // We are not interested in this device + //------------------------------------------------------------------------------------------------------- +} + +// Poll and process evdev keyboard events +static void PollKeyboardEvents(void) +{ + // Scancode to keycode mapping for US keyboards + // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard: + // Currently non US keyboards will have the wrong mapping for some keys + static const int keymapUS[] = { + 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, + 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96, + 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325, + 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95, + 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 + }; + + int fd = CORE.Input.Keyboard.fd; + if (fd == -1) return; + + struct input_event event = { 0 }; + int keycode = -1; + + // Try to read data from the keyboard and only continue if successful + while (read(fd, &event, sizeof(event)) == (int)sizeof(event)) + { + // Button parsing + if (event.type == EV_KEY) + { +#if defined(SUPPORT_SSH_KEYBOARD_RPI) + // Change keyboard mode to events + CORE.Input.Keyboard.evtMode = true; +#endif + // Keyboard button parsing + if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 + { + keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the appropriate keycode + + // Make sure we got a valid keycode + if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) + { + // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt + // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL, + // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat + CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0; + if (event.value >= 1) + { + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + #if defined(SUPPORT_SCREEN_CAPTURE) + // Check screen capture key (raylib key: KEY_F12) + if (CORE.Input.Keyboard.currentKeyState[301] == 1) + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + #endif + + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; + + TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", (event.value == 0)? "UP" : "DOWN", event.code, keycode); + } + } + } + } +} + +// Input device events reading thread +static void *EventThread(void *arg) +{ + struct input_event event = { 0 }; + InputEventWorker *worker = (InputEventWorker *)arg; + + int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE + bool gestureUpdate = false; // Flag to note gestures require to update + + while (!CORE.Window.shouldClose) + { + // Try to read data from the device and only continue if successful + while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) + { + // Relative movement parsing + if (event.type == EV_REL) + { + if (event.code == REL_X) + { + CORE.Input.Mouse.currentPosition.x += event.value; + CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + if (event.code == REL_Y) + { + CORE.Input.Mouse.currentPosition.y += event.value; + CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value; + } + + // Absolute movement parsing + if (event.type == EV_ABS) + { + // Basic movement + if (event.code == ABS_X) + { + CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + if (event.code == ABS_Y) + { + CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + // Multitouch movement + if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remember the slot number for the folowing events + + if (event.code == ABS_MT_POSITION_X) + { + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + } + + if (event.code == ABS_MT_POSITION_Y) + { + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + } + + if (event.code == ABS_MT_TRACKING_ID) + { + if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS)) + { + // Touch has ended for this point + CORE.Input.Touch.position[worker->touchSlot].x = -1; + CORE.Input.Touch.position[worker->touchSlot].y = -1; + } + } + + // Touchscreen tap + if (event.code == ABS_PRESSURE) + { + int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; + + if (!event.value && previousMouseLeftButtonState) + { + CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; + + touchAction = 0; // TOUCH_ACTION_UP + gestureUpdate = true; + } + + if (event.value && !previousMouseLeftButtonState) + { + CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; + + touchAction = 1; // TOUCH_ACTION_DOWN + gestureUpdate = true; + } + } + + } + + // Button parsing + if (event.type == EV_KEY) + { + // Mouse button parsing + if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) + { + CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; + + if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN + else touchAction = 0; // TOUCH_ACTION_UP + gestureUpdate = true; + } + + if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; + if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; + if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; + if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; + if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; + if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value; + } + + // Screen confinement + if (!CORE.Input.Mouse.cursorHidden) + { + if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0; + 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; + + if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0; + 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; + } + + // Update touch point count + CORE.Input.Touch.pointCount = 0; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++; + } + +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM + if (gestureUpdate) + { + GestureEvent gestureEvent = { 0 }; + + gestureEvent.touchAction = touchAction; + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + gestureEvent.pointId[i] = i; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + } + + ProcessGestureEvent(gestureEvent); + } +#endif + } + + WaitTime(0.005); // Sleep for 5ms to avoid hogging CPU time + } + + close(worker->fd); + + return NULL; +} + +// Initialize gamepad system +static void InitGamepad(void) +{ + char gamepadDev[128] = { 0 }; + + for (int i = 0; i < MAX_GAMEPADS; i++) + { + sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i); + + if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) + { + // NOTE: Only show message for first gamepad + if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available"); + } + else + { + CORE.Input.Gamepad.ready[i] = true; + + // NOTE: Only create one thread + if (i == 0) + { + int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL); + + if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); + else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); + } + } + } +} + +// Process Gamepad (/dev/input/js0) +static void *GamepadThread(void *arg) +{ + #define JS_EVENT_BUTTON 0x01 // Button pressed/released + #define JS_EVENT_AXIS 0x02 // Joystick axis moved + #define JS_EVENT_INIT 0x80 // Initial state of device + + struct js_event { + unsigned int time; // event timestamp in milliseconds + short value; // event value + unsigned char type; // event type + unsigned char number; // event axis/button number + }; + + // Read gamepad event + struct js_event gamepadEvent = { 0 }; + + while (!CORE.Window.shouldClose) + { + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) + { + gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events + + // Process gamepad events by type + if (gamepadEvent.type == JS_EVENT_BUTTON) + { + //TRACELOG(LOG_WARNING, "RPI: Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value); + + if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS) + { + // 1 - button pressed, 0 - button released + CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value; + + if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number; + else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + } + } + else if (gamepadEvent.type == JS_EVENT_AXIS) + { + //TRACELOG(LOG_WARNING, "RPI: Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value); + + if (gamepadEvent.number < MAX_GAMEPAD_AXIS) + { + // NOTE: Scaling of gamepadEvent.value to get values between -1..1 + CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768; + } + } + } + else WaitTime(0.001); // Sleep for 1 ms to avoid hogging CPU time + } + } + + return NULL; +} + +// Search matching DRM mode in connector's mode list +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, "DISPLAY: DRM 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])) return i; + } + + return -1; + + #undef BINCMP +} + +// Search exactly matching DRM connector mode in connector's list +static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) +{ + 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"); + + 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, "DISPLAY: DRM 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)) continue; + + if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i; + } + + TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found"); + return -1; +} + +// Search the nearest matching DRM connector mode in connector's list +static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) +{ + 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"); + + 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, "DISPLAY: DRM 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)) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small"); + continue; + } + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode"); + continue; + } + + if (nearestIndex < 0) + { + nearestIndex = i; + continue; + } + + const int widthDiff = abs(mode->hdisplay - width); + const int heightDiff = abs(mode->vdisplay - height); + const int fpsDiff = abs(mode->vrefresh - fps); + + const int nearestWidthDiff = abs(CORE.Window.connector->modes[nearestIndex].hdisplay - width); + const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height); + const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps); + + if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) { + nearestIndex = i; + } + } + + return nearestIndex; +} + +// EOF diff --git a/src/rcore_web.c b/src/rcore_web.c new file mode 100644 index 000000000..baafd60d6 --- /dev/null +++ b/src/rcore_web.c @@ -0,0 +1,1604 @@ +/********************************************************************************************** +* +* rcore_web - Functions to manage window, graphics device and inputs +* +* PLATFORM: WEB +* - HTML5 (WebAssembly) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Replace glfw3 dependency by direct browser API calls (same as library_glfw3.js) +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_WEB_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_WEB -not used- +* +* DEPENDENCIES: +* emscripten - Allow interaction between browser API and C +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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 "rcore.h" + +#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) +// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) +#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management +#include // Required for: timespec, nanosleep(), select() - POSIX + +#include // Emscripten functionality for C +#include // Emscripten HTML5 library + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +// Error callback event +static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error + +// Window callbacks events +static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized +static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window + +// Input callbacks events +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed +static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel +static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area + +// Emscripten callback events +static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData); +static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData); +static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); + +static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); +static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); +static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f}; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + // Initialize graphics device (display device and OpenGL context) + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); +#if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 2, rec.y + 2, 1, 1}); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2}); + } +#endif +#else +#if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = {rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8}; + SetShapesTexture(texture, (Rectangle){0.0f, 0.0f, 1.0f, 1.0f}); // WARNING: Module required: rshapes +#endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + + // Setup callback functions for the DOM events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); + + // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review + // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) + // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + + // Trigger this once to get initial window sizing + EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); + + // Support keyboard events -> Not used, GLFW.JS takes care of that + // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + + // Support mouse events + emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); + + // Support touch events + emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + + // Support gamepad events (not provided by GLFW3 on emscripten) + emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + + glfwDestroyWindow(CORE.Window.handle); + glfwTerminate(); + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + // Emterpreter-Async required to run sync code + // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code + // By default, this function is never called on a web-ready raylib example because we encapsulate + // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously + // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter! + emscripten_sleep(16); + return false; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return CORE.Window.resizedLastFrame; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + /* + EM_ASM + ( + // This strategy works well while using raylib minimal web shell for emscripten, + // it re-scales the canvas to fullscreen using monitor resolution, for tools this + // is a good strategy but maybe games prefer to keep current canvas resolution and + // display it in fullscreen, adjusting monitor resolution if possible + if (document.fullscreenElement) document.exitFullscreen(); + else Module.requestFullscreen(true, true); //false, true); + ); + */ + // EM_ASM(Module.requestFullscreen(false, false);); + /* + if (!CORE.Window.fullscreen) + { + // Option 1: Request fullscreen for the canvas element + // This option does not seem to work at all: + // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security, + // the user must click once on the canvas to hide the pointer or transition to full screen + //emscripten_request_fullscreen("#canvas", false); + + // Option 2: Request fullscreen for the canvas element with strategy + // This option does not seem to work at all + // Ref: https://github.com/emscripten-core/emscripten/issues/5124 + // EmscriptenFullscreenStrategy strategy = { + // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, + // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, + // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, + // .canvasResizedCallback = EmscriptenWindowResizedCallback, + // .canvasResizedCallbackUserData = NULL + // }; + //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy); + + // Option 3: Request fullscreen for the canvas element with strategy + // It works as expected but only inside the browser (client area) + EmscriptenFullscreenStrategy strategy = { + .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, + .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, + .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, + .canvasResizedCallback = EmscriptenWindowResizedCallback, + .canvasResizedCallbackUserData = NULL + }; + emscripten_enter_soft_fullscreen("#canvas", &strategy); + + int width, height; + emscripten_get_canvas_element_size("#canvas", &width, &height); + TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height); + + CORE.Window.fullscreen = true; // Toggle fullscreen flag + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + } + else + { + //emscripten_exit_fullscreen(); + //emscripten_exit_soft_fullscreen(); + + int width, height; + emscripten_get_canvas_element_size("#canvas", &width, &height); + TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height); + + CORE.Window.fullscreen = false; // Toggle fullscreen flag + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + } + */ + + CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_WEB"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_WEB"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_WEB"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_WEB"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_WEB"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_WEB"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_WEB"); +} + +// Set icon for window, multiple images +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_WEB"); +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; + emscripten_set_window_title(title); +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_WEB"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_WEB"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; + + // Trigger the resize event once to update the window minimum width and height + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; + + // Trigger the resize event once to update the window maximum width and height + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + glfwSetWindowSize(CORE.Window.handle, width, height); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_WEB"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_WEB"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_WEB"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_WEB"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_WEB"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_WEB"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_WEB"); + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + // Security check to (partially) avoid malicious code + if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character"); + else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ +/* + // Accessing clipboard data from browser is tricky due to security reasons + // The method to use is navigator.clipboard.readText() but this is an asynchronous method + // that will return at some moment after the function is called with the required data + emscripten_run_script_string("navigator.clipboard.readText() \ + .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \ + .catch(err => { console.error('Failed to read clipboard contents: ', err); });" + ); + + // The main issue is getting that data, one approach could be using ASYNCIFY and wait + // for the data but it requires adding Asyncify emscripten library on compilation + + // Another approach could be just copy the data in a HTML text field and try to retrieve it + // later on if available... and clean it for future accesses +*/ + return NULL; +} + +// 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) +{ + emscripten_exit_pointerlock(); + + // 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) +{ + // TODO: figure out how not to hard code the canvas ID here. + emscripten_request_pointerlock("#canvas", 1); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = glfwGetTime(); // Elapsed time since glfwInit() + return time; +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_WEB + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + // Download file from MEMFS (emscripten memory filesystem) + // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html + emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code on PLATFORM_WEB + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else emscripten_run_script(TextFormat("window.open('%s', '_blank')", url)); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +// NOTE: default exitKey is ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + const char *name = NULL; + + name = CORE.Input.Gamepad.name[gamepad]; + + return name; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_INFO, "SetGamepadMappings not implemented in rcore_web.c"); + + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + Vector2 position = { 0 }; + + // TODO: Review touch position + + // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; +} + +// 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; + + // NOTE: emscripten not implemented + glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + float result = 0.0f; + + if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; + else result = (float)CORE.Input.Mouse.currentWheelMove.y; + + return result; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_INFO, "SetMouseCursor not implemented in rcore_web.c"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + 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); + + return position; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(CORE.Window.handle); +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) + + // Register previous keys states + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Register previous mouse states + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + + // Register previous mouse wheel state + CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; + CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; + + // Register previous mouse position + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + CORE.Window.resizedLastFrame = false; + + // Gamepad support using emscripten API + // NOTE: GLFW3 joystick functionality not available in web + + // Get number of gamepads connected + int numGamepads = 0; + if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads(); + + for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++) + { + // Register previous gamepad button states + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; + + EmscriptenGamepadEvent gamepadState; + + int result = emscripten_get_gamepad_status(i, &gamepadState); + + if (result == EMSCRIPTEN_RESULT_SUCCESS) + { + // Register buttons data for every connected gamepad + for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++) + { + GamepadButton button = -1; + + // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface + switch (j) + { + case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; + case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; + case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break; + case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; + case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + default: break; + } + + if (button != -1) // Check for valid button + { + if (gamepadState.digitalButton[j] == 1) + { + CORE.Input.Gamepad.currentButtonState[i][button] = 1; + CORE.Input.Gamepad.lastButtonPressed = button; + } + else CORE.Input.Gamepad.currentButtonState[i][button] = 0; + } + + //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); + } + + // Register axis data for every connected gamepad + for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++) + { + CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; + } + + CORE.Input.Gamepad.axisCount = gamepadState.numAxes; + } + } +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; + + glfwInitAllocator(&allocator); +*/ + + if (!glfwInit()) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); + return false; + } + + glfwDefaultWindowHints(); // Set default windows hints + // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits + // glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits + // glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits + // glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits + // glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits + // glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window + // glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API + // glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers + + // Check window creation flags + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true; + + if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window + else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden + + if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window + else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window + + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window + else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable + + // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + + // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); + else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); + + if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); + else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); + + // NOTE: Some GLFW flags are not supported on HTML5 + // e.g.: GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_SCALE_TO_MONITOR, GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_MOUSE_PASSTHROUGH + + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 + } + + // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version + // with backward compatibility to older OpenGL versions. + // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context. + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) + } + else if (rlGetVersion() == RL_OPENGL_33) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! + // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! + // glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context + } + else if (rlGetVersion() == RL_OPENGL_43) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + } + + // NOTE: Getting video modes is not implemented in emscripten GLFW3 version + CORE.Window.display.width = CORE.Window.screen.width; + CORE.Window.display.height = CORE.Window.screen.height; + + if (CORE.Window.fullscreen) + { + // remember center for switchinging from fullscreen to window + if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) + { + // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. + // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11. + CORE.Window.position.x = CORE.Window.display.width/4; + CORE.Window.position.y = CORE.Window.display.height/4; + } + else + { + CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2; + CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2; + } + + if (CORE.Window.position.x < 0) CORE.Window.position.x = 0; + if (CORE.Window.position.y < 0) CORE.Window.position.y = 0; + + // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor + int count = 0; + const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); + + // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height + for (int i = 0; i < count; i++) + { + if ((unsigned int)modes[i].width >= CORE.Window.screen.width) + { + if ((unsigned int)modes[i].height >= CORE.Window.screen.height) + { + CORE.Window.display.width = modes[i].width; + CORE.Window.display.height = modes[i].height; + break; + } + } + } + + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + + // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, + // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), + // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched + // by the sides to fit all monitor space... + + // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight + // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset) + // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale + // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed... + // HighDPI monitors are properly considered in a following similar function: SetupViewport() + SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + + CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + + // NOTE: Full-screen change, not working properly... + // glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + else + { + // No-fullscreen window creation + CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + + if (CORE.Window.handle) + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + } + } + + if (!CORE.Window.handle) + { + glfwTerminate(); + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); + return false; + } + + // WARNING: glfwCreateWindow() title doesn't work with emscripten + emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); + + // Set window callback events + glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); + glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + + // Set input callback events + glfwSetKeyCallback(CORE.Window.handle, KeyCallback); + glfwSetCharCallback(CORE.Window.handle, CharCallback); + glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); + glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + + glfwMakeContextCurrent(CORE.Window.handle); + + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need + // to be activated on web platforms since VSync is enforced there. + + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + CORE.Window.currentFbo.width = fbWidth; + CORE.Window.currentFbo.height = fbHeight; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// GLFW3 Error Callback, runs on GLFW3 error +static void ErrorCallback(int error, const char *description) +{ + TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description); +} + +// GLFW3 WindowSize Callback, runs when window is resizedLastFrame +// NOTE: Window resizing not allowed by default +static void WindowSizeCallback(GLFWwindow *window, int width, int height) +{ + // Reset viewport and projection matrix for new size + SetupViewport(width, height); + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return; + + // Set current screen size + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + Vector2 windowScaleDPI = GetWindowScaleDPI(); + + CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x); + CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y); + } + else + { + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + } + + // NOTE: Postprocessing texture is not scaled to new size +} + +// GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowIconifyCallback(GLFWwindow *window, int iconified) +{ + if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified + else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored +} + +// GLFW3 Window Maximize Callback, runs when window is maximized +static void WindowMaximizeCallback(GLFWwindow *window, int maximized) +{ + +} + +// GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowFocusCallback(GLFWwindow *window, int focused) +{ + if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused + else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus +} + +// GLFW3 Window Drop Callback, runs when drop files into window +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) +{ + if (count > 0) + { + // In case previous dropped filepaths have not been freed, we free them + if (CORE.Window.dropFileCount > 0) + { + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); + + RL_FREE(CORE.Window.dropFilepaths); + + CORE.Window.dropFileCount = 0; + CORE.Window.dropFilepaths = NULL; + } + + // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy + CORE.Window.dropFileCount = count; + CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); + + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) + { + CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[i], paths[i]); + } + } +} + + +// GLFW3 Keyboard Callback, runs on key pressed +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) +{ + if (key < 0) return; // Security check, macOS fn key generates -1 + + // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 + // to work properly with our implementation (IsKeyDown/IsKeyUp checks) + if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; + else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; + else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; + + // Check if there is space available in the key queue + if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) + { + // Add character to the queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + // Check the exit key to set close window + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + +#if defined(SUPPORT_SCREEN_CAPTURE) + if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) + { +#if defined(SUPPORT_GIF_RECORDING) + if (mods & GLFW_MOD_CONTROL) + { + if (gifRecording) + { + gifRecording = false; + + MsfGifResult result = msf_gif_end(&gifState); + + SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); + msf_gif_free(result); + + // Download file from MEMFS (emscripten memory filesystem) + // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html + emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); + + TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); + } + else + { + gifRecording = true; + gifFrameCounter = 0; + + Vector2 scale = GetWindowScaleDPI(); + msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + screenshotCounter++; + + TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); + } + } + else +#endif // SUPPORT_GIF_RECORDING + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + } +#endif // SUPPORT_SCREEN_CAPTURE + +#if defined(SUPPORT_EVENTS_AUTOMATION) + if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) + { + eventsRecording = !eventsRecording; + + // On finish recording, we export events into a file + if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); + } + else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) + { + LoadAutomationEvents("eventsrec.rep"); + eventsPlaying = true; + + TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); + } +#endif +} + +// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) +static void CharCallback(GLFWwindow *window, unsigned int key) +{ + //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key); + + // NOTE: Registers any key down considering OS keyboard layout but + // does not detect action events, those should be managed by user... + // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907 + // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char + + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; + CORE.Input.Keyboard.charPressedQueueCount++; + } +} + +// GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) +{ + // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now, + // but future releases may add more actions (i.e. GLFW_REPEAT) + CORE.Input.Mouse.currentButtonState[button] = action; + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + // Register touch actions + if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP; + + // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback() + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = GetMousePosition(); + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + // Prevent calling ProcessGestureEvent() when Emscripten is present and there's a touch gesture, so EmscriptenTouchCallback() can handle it itself + if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent); + +#endif +} + +// GLFW3 Cursor Position Callback, runs on mouse move +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) +{ + CORE.Input.Mouse.currentPosition.x = (float)x; + CORE.Input.Mouse.currentPosition.y = (float)y; + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + gestureEvent.touchAction = TOUCH_ACTION_MOVE; + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = CORE.Input.Touch.position[0]; + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); +#endif +} + +// GLFW3 Scrolling Callback, runs on mouse wheel +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) +{ + CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; +} + +// GLFW3 CursorEnter Callback, when cursor enters the window +static void CursorEnterCallback(GLFWwindow *window, int enter) +{ + if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + else CORE.Input.Mouse.cursorOnScreen = false; +} + + +// Register fullscreen change events +static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) +{ + // TODO: Implement EmscriptenFullscreenChangeCallback()? + + return 1; // The event was consumed by the callback handler +} + +// Register window resize event +static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData) +{ + // TODO: Implement EmscriptenWindowResizedCallback()? + + return 1; // The event was consumed by the callback handler +} + +EM_JS(int, GetWindowInnerWidth, (), { return window.innerWidth; }); +EM_JS(int, GetWindowInnerHeight, (), { return window.innerHeight; }); + +// Register DOM element resize event +static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData) +{ + // Don't resize non-resizeable windows + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1; + + // This event is called whenever the window changes sizes, + // so the size of the canvas object is explicitly retrieved below + int width = GetWindowInnerWidth(); + int height = GetWindowInnerHeight(); + + if (width < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; + else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width; + + if (height < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; + else if (height > CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height; + + emscripten_set_canvas_element_size("#canvas", width, height); + + SetupViewport(width, height); // Reset viewport and projection matrix for new size + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return 1; + + // Set current screen size + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + + // NOTE: Postprocessing texture is not scaled to new size + + return 0; +} + +// Register mouse input events +static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) +{ + // This is only for registering mouse click events with emscripten and doesn't need to do anything + + return 1; // The event was consumed by the callback handler +} + +// Register connected/disconnected gamepads events +static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) +{ + /* + TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", + eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", + gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); + + for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]); + for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + */ + + if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) + { + CORE.Input.Gamepad.ready[gamepadEvent->index] = true; + sprintf(CORE.Input.Gamepad.name[gamepadEvent->index], "%s", gamepadEvent->id); + } + else CORE.Input.Gamepad.ready[gamepadEvent->index] = false; + + return 1; // The event was consumed by the callback handler +} + +// Register touch input events +static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) +{ + // Register touch points count + CORE.Input.Touch.pointCount = touchEvent->numTouches; + + double canvasWidth = 0.0; + double canvasHeight = 0.0; + // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but + // we are looking for actual CSS size: canvas.style.width and canvas.style.height + // EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight); + emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight); + + for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + // Register touch points id + CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier; + + // Register touch points position + CORE.Input.Touch.position[i] = (Vector2){touchEvent->touches[i].targetX, touchEvent->touches[i].targetY}; + + // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height + CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth); + CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight); + + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0; + } + +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB + GestureEvent gestureEvent = {0}; + + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + + // Register touch actions + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; + + for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + + // Normalize gestureEvent.position[i] + gestureEvent.position[i].x /= (float)GetScreenWidth(); + gestureEvent.position[i].y /= (float)GetScreenHeight(); + } + + // Gesture data is sent to gestures system for processing + ProcessGestureEvent(gestureEvent); + + // Reset the pointCount for web, if it was the last Touch End event + if (eventType == EMSCRIPTEN_EVENT_TOUCHEND && CORE.Input.Touch.pointCount == 1) CORE.Input.Touch.pointCount = 0; +#endif + + return 1; // The event was consumed by the callback handler +} + +// EOF From bbbaae55621a854a6e69bcb9cfe1c6a5a8697e2c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 23:38:52 +0200 Subject: [PATCH 0002/1037] Reviewed #3313 --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 44b7ee787..ff7558dc3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1050,7 +1050,7 @@ Ray GetMouseRay(Vector2 mouse, Camera camera) } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + double aspect = (double)CORE.Window.screen.width/(double)CORE.Window.screen.height; double top = camera.fovy/2.0; double right = top*aspect; @@ -1134,7 +1134,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + double aspect = (double)width/(double)height; double top = camera.fovy/2.0; double right = top*aspect; From d445fdaa199eae6540621b5a35f2d498f5ffab5c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 00:41:06 +0200 Subject: [PATCH 0003/1037] WARNING: REDESIGN: Move platform specific data to platform submodules #3313 REVIEWED: Defines, macros, types and tweaks --- src/rcore.c | 70 +++----- src/rcore.h | 149 +++-------------- src/rcore_android.c | 156 +++++++++-------- src/rcore_desktop.c | 189 +++++++++++---------- src/rcore_drm.c | 398 ++++++++++++++++++++++++++------------------ src/rcore_web.c | 75 +++++---- src/utils.h | 2 +- 7 files changed, 519 insertions(+), 520 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index ff7558dc3..f28aff74f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1,33 +1,18 @@ /********************************************************************************************** * -* rcore - Basic functions to manage windows, OpenGL context and input on multiple platforms +* rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: * - PLATFORM_DESKTOP: Windows (Win32, Win64) * - PLATFORM_DESKTOP: Linux (X11 desktop mode) * - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) * - PLATFORM_DESKTOP: OSX/macOS +* - PLATFORM_WEB: HTML5 (WebAssembly) +* - PLATFORM_DRM: Raspberry Pi 0-5 +* - PLATFORM_DRM: Linux native mode (KMS driver) * - PLATFORM_ANDROID: Android (ARM, ARM64) -* - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver -* - PLATFORM_WEB: HTML5 with WebAssembly * * CONFIGURATION: -* #define PLATFORM_DESKTOP -* Windowing and input system configured for desktop platforms: -* Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly -* -* #define PLATFORM_ANDROID -* Windowing and input system configured for Android device, app activity managed internally in this module. -* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL -* -* #define PLATFORM_DRM -* Windowing and input system configured for DRM native mode (RPI4 and other devices) -* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ -* -* #define PLATFORM_WEB -* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js -* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code. -* * #define SUPPORT_DEFAULT_FONT (default) * Default font is loaded on window initialization to be available for the user to render simple text. * NOTE: If enabled, uses external module functions to load default raylib font (module: text) @@ -42,11 +27,6 @@ * #define SUPPORT_MOUSE_GESTURES * Mouse gestures are directly mapped like touches and processed by gestures system. * -* #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only) -* Reconfigure standard input to receive key inputs, works with SSH connection. -* WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other -* running processes orblocking the device if not restored properly. Use with care. -* * #define SUPPORT_BUSY_WAIT_LOOP * Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used * @@ -68,7 +48,6 @@ * Support automatic generated events, loading and recording of those events when required * * DEPENDENCIES: -* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX, FreeBSD...) * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) * camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person) * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) @@ -76,7 +55,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 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. @@ -102,7 +81,7 @@ #include "config.h" // Defines module configuration flags #endif -#include "rcore.h" +#include "rcore.h" // Defines types and globals #define RLGL_IMPLEMENTATION #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 @@ -142,21 +121,19 @@ #endif // Platform specific defines to handle GetApplicationDirectory() -#if defined (PLATFORM_DESKTOP) - #if defined(_WIN32) - #ifndef MAX_PATH - #define MAX_PATH 1025 - #endif - __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize); - __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize); - __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default); - #elif defined(__linux__) - #include - #elif defined(__APPLE__) - #include - #include - #endif // OSs -#endif // PLATFORM_DESKTOP +#if defined(_WIN32) + #ifndef MAX_PATH + #define MAX_PATH 1025 + #endif +__declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize); +__declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize); +__declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default); +#elif defined(__linux__) + #include +#elif defined(__APPLE__) + #include + #include +#endif // OSs #define _CRT_INTERNAL_NONSTDC_NAMES 1 #include // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()] @@ -165,7 +142,7 @@ #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif -#if defined(PLATFORM_DESKTOP) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__)) +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__)) #define DIRENT_MALLOC RL_MALLOC #define DIRENT_FREE RL_FREE @@ -333,7 +310,8 @@ const char *TextFormat(const char *text, ...); // Formatting of text with #elif defined(PLATFORM_ANDROID) #include "rcore_android.c" #else - // Software rendering backend, user needs to provide buffer ;) + // TODO: Include your custom platform backend! + // i.e software rendering backend or console backend! #endif //---------------------------------------------------------------------------------- @@ -1897,7 +1875,7 @@ bool IsKeyPressed(int key) return pressed; } -// Check if a key has been pressed again (only PLATFORM_DESKTOP) +// Check if a key has been pressed again bool IsKeyPressedRepeat(int key) { bool repeat = false; @@ -2291,7 +2269,7 @@ void InitTimer(void) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) struct timespec now = { 0 }; if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success diff --git a/src/rcore.h b/src/rcore.h index 5d9e3bd74..132c8e007 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -35,64 +35,6 @@ #ifndef RCORE_H #define RCORE_H -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() -#include // Required for: time() [Used in InitTimer()] -#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] - -#include "utils.h" // Required for: TRACELOG() macros - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 - // NOTE: Already provided by rlgl implementation (on glad.h) - #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management - // NOTE: GLFW3 already includes gl.h (OpenGL) headers -#endif - -#if defined(PLATFORM_ANDROID) - #include // Native platform windowing system interface - //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) -#endif - -#if defined(PLATFORM_DRM) - - #include // POSIX file control definitions - open(), creat(), fcntl() - #include // POSIX standard function definitions - read(), close(), STDIN_FILENO - #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() - #include // POSIX threads management (inputs reading) - #include // POSIX directory browsing - - #include // Required for: ioctl() - UNIX System call for device-specific input/output operations - #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition - #include // Linux: Keycodes constants definition (KEY_A, ...) - #include // Linux: Joystick support library - - #include // Generic Buffer Management (native platform for EGL on DRM) - #include // Direct Rendering Manager user-level library interface - #include // Direct Rendering Manager mode setting (KMS) interface - - #include "EGL/egl.h" // Native platform windowing system interface - #include "EGL/eglext.h" // EGL extensions - - typedef struct - { - pthread_t threadId; // Event reading thread id - int fd; // File descriptor to the device it is assigned to - int eventNum; // Number of 'event' device - Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) - int touchSlot; // Hold the touch slot number of the currently being sent multitouch block - bool isMouse; // True if device supports relative X Y movements - bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH - bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH - bool isKeyboard; // True if device has letter keycodes - bool isGamepad; // True if device has gamepad buttons - } InputEventWorker; - -#endif - -// TODO: PROVIDE A HEADER TO BE USED BY ALL THE rcore_* IMPLEMENTATIONS - #include "raylib.h" #include "rlgl.h" @@ -100,16 +42,17 @@ #define RAYMATH_IMPLEMENTATION #include "raymath.h" +#include "utils.h" // Required for: TRACELOG() macros + +#include // Required for: srand(), rand(), atexit() +#include // Required for: sprintf() [Used in OpenURL()] +#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: time() [Used in InitTimer()] +#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#if defined(PLATFORM_DRM) - #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number - - #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) - #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events -#endif - #ifndef MAX_FILEPATH_CAPACITY #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath #endif @@ -152,12 +95,6 @@ #define FLAG_TOGGLE(n, f) ((n) ^= (f)) #define FLAG_CHECK(n, f) ((n) & (f)) -// TODO: HACK: Added flag if not provided by GLFW when using external library -// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev -#if !defined(GLFW_MOUSE_PASSTHROUGH) - #define GLFW_MOUSE_PASSTHROUGH 0x0002000D -#endif - #if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. @@ -172,25 +109,6 @@ typedef struct { unsigned int width; unsigned int height; } Size; // Core global state context data typedef struct CoreData { struct { -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - GLFWwindow *handle; // GLFW window handle (graphic device) -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) -#if defined(PLATFORM_DRM) - int fd; // File descriptor for /dev/dri/... - drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector - drmModeCrtc *crtc; // CRT Controller - int modeIndex; // Index of the used mode of connector->modes - struct gbm_device *gbmDevice; // GBM device - struct gbm_surface *gbmSurface; // GBM surface - struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) - uint32_t prevFB; // Previous GBM framebufer (during frame swapping) -#endif // PLATFORM_DRM - 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 - EGLConfig config; // Graphic config -#endif const char *title; // Window text title const pointer unsigned int flags; // Configuration flags (bit based), keeps window state bool ready; // Check if window has been initialized successfully @@ -215,45 +133,27 @@ typedef struct CoreData { char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings - + } Window; -#if defined(PLATFORM_ANDROID) - struct { - bool appEnabled; // Flag to detect if app is active ** = true - struct android_app *app; // Android activity - struct android_poll_source *source; // Android events polling source - bool contextRebindRequired; // Used to know context rebind required - } Android; -#endif struct { const char *basePath; // Base path for data storage + } Storage; struct { -#if defined(PLATFORM_DRM) - InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" -#endif struct { int exitKey; // Default exit key - char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state - char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state + char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially - char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. + char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. - int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue + int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue int keyPressedQueueCount; // Input keys queue count - int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) + int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) int charPressedQueueCount; // Input characters queue count -#if defined(PLATFORM_DRM) - int defaultMode; // Default keyboard mode -#if defined(SUPPORT_SSH_KEYBOARD_RPI) - bool evtMode; // Keyboard in event mode -#endif - int defaultFileFlags; // Default IO file flags - struct termios defaultSettings; // Default keyboard settings - int fd; // File descriptor for the evdev keyboard -#endif } Keyboard; struct { Vector2 offset; // Mouse offset @@ -269,11 +169,7 @@ typedef struct CoreData { char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state Vector2 currentWheelMove; // Registers current mouse wheel variation Vector2 previousWheelMove; // Registers previous mouse wheel variation -#if defined(PLATFORM_DRM) - Vector2 eventWheelMove; // Registers the event mouse wheel variation - // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update - char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab -#endif + } Mouse; struct { int pointCount; // Number of touch points active @@ -281,6 +177,7 @@ typedef struct CoreData { Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state + } Touch; struct { int lastButtonPressed; // Register last gamepad button pressed @@ -290,10 +187,7 @@ typedef struct CoreData { char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state -#if defined(PLATFORM_DRM) - pthread_t threadId; // Gamepad reading thread id - int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor -#endif + } Gamepad; } Input; struct { @@ -303,10 +197,9 @@ 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_DRM) - unsigned long long int base; // Base time measure for hi-res timer -#endif + unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) unsigned int frameCounter; // Frame counter + } Time; } CoreData; diff --git a/src/rcore_android.c b/src/rcore_android.c index 61d729a1c..ad7a905c1 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -53,15 +53,31 @@ #include // Required for: android_app struct and activity management #include // Required for: JNIEnv and JavaVM [Used in OpenURL()] +#include // Native platform windowing system interface + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... +typedef struct { + // Application data + struct android_app *app; // Android activity + struct android_poll_source *source; // Android events polling source + bool appEnabled; // Flag to detect if app is active ** = true + bool contextRebindRequired; // Used to know context rebind required + + // Display data + 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 + EGLConfig config; // Graphic config +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -89,7 +105,7 @@ extern int main(int argc, char *argv[]); void android_main(struct android_app *app) { char arg0[] = "raylib"; // NOTE: argv[] are mutable - CORE.Android.app = app; + platform.app = app; // NOTE: Return from main is ignored (void)main(1, (char *[]) { arg0, NULL }); @@ -104,9 +120,9 @@ void android_main(struct android_app *app) // Waiting for application events before complete finishing while (!app->destroyRequested) { - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&platform.source)) >= 0) { - if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); + if (platform.source != NULL) platform.source->process(app, platform.source); } } } @@ -114,7 +130,7 @@ void android_main(struct android_app *app) // NOTE: Add this to header (if apps really need it) struct android_app *GetAndroidApp(void) { - return CORE.Android.app; + return platform.app; } // Initialize window and OpenGL context @@ -169,9 +185,9 @@ void InitWindow(int width, int height, const char *title) CORE.Window.currentFbo.height = height; // Set desired windows flags before initializing anything - ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER - int orientation = AConfiguration_getOrientation(CORE.Android.app->config); + int orientation = AConfiguration_getOrientation(platform.app->config); if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); @@ -179,32 +195,32 @@ void InitWindow(int width, int height, const char *title) // TODO: Automatic orientation doesn't seem to work if (width <= height) { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT); + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); } else { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND); + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND); TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); } - //AConfiguration_getDensity(CORE.Android.app->config); - //AConfiguration_getKeyboard(CORE.Android.app->config); - //AConfiguration_getScreenSize(CORE.Android.app->config); - //AConfiguration_getScreenLong(CORE.Android.app->config); + //AConfiguration_getDensity(platform.app->config); + //AConfiguration_getKeyboard(platform.app->config); + //AConfiguration_getScreenSize(platform.app->config); + //AConfiguration_getScreenLong(platform.app->config); // Initialize App command system // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - CORE.Android.app->onAppCmd = AndroidCommandCallback; + platform.app->onAppCmd = AndroidCommandCallback; // Initialize input events system - CORE.Android.app->onInputEvent = AndroidInputCallback; + platform.app->onInputEvent = AndroidInputCallback; // Initialize assets manager - InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath); + InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize base path for storage - CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath; + CORE.Storage.basePath = platform.app->activity->internalDataPath; TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); @@ -216,13 +232,13 @@ void InitWindow(int width, int height, const char *title) while (!CORE.Window.ready) { // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) { // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + if (platform.source != NULL) platform.source->process(platform.app, platform.source); // NOTE: Never close window, native activity is controlled by the system! - //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true; + //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; } } } @@ -250,24 +266,24 @@ void CloseWindow(void) #endif // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) + if (platform.device != EGL_NO_DISPLAY) { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (CORE.Window.surface != EGL_NO_SURFACE) + if (platform.surface != EGL_NO_SURFACE) { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; } - if (CORE.Window.context != EGL_NO_CONTEXT) + if (platform.context != EGL_NO_CONTEXT) { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; } - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; } #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -306,7 +322,7 @@ bool IsWindowMaximized(void) // Check if window has the focus bool IsWindowFocused(void) { - return CORE.Android.appEnabled; + return platform.appEnabled; } // Check if window has been resizedLastFrame @@ -595,7 +611,7 @@ void OpenURL(const char *url) else { JNIEnv *env = NULL; - JavaVM *vm = CORE.Android.app->activity->vm; + JavaVM *vm = platform.app->activity->vm; (*vm)->AttachCurrentThread(vm, &env, NULL); jstring urlString = (*env)->NewStringUTF(env, url); @@ -612,7 +628,7 @@ void OpenURL(const char *url) (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); - (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent); + (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent); (*vm)->DetachCurrentThread(vm); } @@ -713,7 +729,7 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - eglSwapBuffers(CORE.Window.device, CORE.Window.surface); + eglSwapBuffers(platform.device, platform.surface); } // Register all input events @@ -756,17 +772,17 @@ void PollInputEvents(void) int pollEvents = 0; // Poll Events (registered events) - // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled) - while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + // NOTE: Activity is paused if not enabled (platform.appEnabled) + while ((pollResult = ALooper_pollAll(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) >= 0) { // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + if (platform.source != NULL) platform.source->process(platform.app, platform.source); // NOTE: Never close window, native activity is controlled by the system! - if (CORE.Android.app->destroyRequested != 0) + if (platform.app->destroyRequested != 0) { //CORE.Window.shouldClose = true; - //ANativeActivity_finish(CORE.Android.app->activity); + //ANativeActivity_finish(platform.app->activity); } } } @@ -829,15 +845,15 @@ static bool InitGraphicsDevice(int width, int height) EGLint numConfigs = 0; // Get an EGL device connection - CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (CORE.Window.device == EGL_NO_DISPLAY) + platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); return false; } // Initialize the EGL device connection - if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) + if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE) { // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); @@ -845,14 +861,14 @@ static bool InitGraphicsDevice(int width, int height) } // Get an appropriate EGL framebuffer configuration - eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs); + eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs); // Set rendering API eglBindAPI(EGL_OPENGL_ES_API); // Create an EGL rendering context - CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); - if (CORE.Window.context == EGL_NO_CONTEXT) + platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs); + if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); return false; @@ -864,7 +880,7 @@ static bool InitGraphicsDevice(int width, int height) // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); // At this point we need to manage render size vs screen size // NOTE: This function use and modify global module variables: @@ -873,15 +889,15 @@ static bool InitGraphicsDevice(int width, int height) // -> CORE.Window.screenScale SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); - //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size + ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); + platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL); // There must be at least one frame displayed before the buffers are swapped - //eglSwapInterval(CORE.Window.device, 1); + //eglSwapInterval(platform.device, 1); - if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); return false; @@ -933,11 +949,11 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) { if (app->window != NULL) { - if (CORE.Android.contextRebindRequired) + if (platform.contextRebindRequired) { // Reset screen scaling to full display size EGLint displayFormat = 0; - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the // context rebinding if the screen is scaled unless offsets are added. There's probably a more @@ -948,15 +964,15 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) displayFormat); // Recreate display surface and re-attach OpenGL context - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); - eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context); + platform.surface = eglCreateWindowSurface(platform.device, platform.config, app->window, NULL); + eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); - CORE.Android.contextRebindRequired = false; + platform.contextRebindRequired = false; } else { - CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window); - CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window); + CORE.Window.display.width = ANativeWindow_getWidth(platform.app->window); + CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window); // Initialize graphics device (display device and OpenGL context) InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); @@ -997,13 +1013,13 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } break; case APP_CMD_GAINED_FOCUS: { - CORE.Android.appEnabled = true; + platform.appEnabled = true; //ResumeMusicStream(); } break; case APP_CMD_PAUSE: break; case APP_CMD_LOST_FOCUS: { - CORE.Android.appEnabled = false; + platform.appEnabled = false; //PauseMusicStream(); } break; case APP_CMD_TERM_WINDOW: @@ -1012,19 +1028,19 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming. // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :( - if (CORE.Window.device != EGL_NO_DISPLAY) + if (platform.device != EGL_NO_DISPLAY) { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (CORE.Window.surface != EGL_NO_SURFACE) + if (platform.surface != EGL_NO_SURFACE) { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; } - CORE.Android.contextRebindRequired = true; + platform.contextRebindRequired = true; } - // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' + // If 'platform.device' is already set to 'EGL_NO_DISPLAY' // this means that the user has already called 'CloseWindow()' } break; @@ -1033,8 +1049,8 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_DESTROY: break; case APP_CMD_CONFIG_CHANGED: { - //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); - //print_cur_config(CORE.Android.app); + //AConfiguration_fromAssetManager(platform.app->config, platform.app->activity->assetManager); + //print_cur_config(platform.app); // Check screen orientation here! } break; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index f8c6b0e2c..33a4aab0e 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -85,15 +85,28 @@ #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() #endif +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// TODO: HACK: Added flag if not provided by GLFW when using external library +// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev +#if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +#endif + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... +typedef struct { + GLFWwindow *handle; // GLFW window handle (graphic device) +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -255,7 +268,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - glfwDestroyWindow(CORE.Window.handle); + glfwDestroyWindow(platform.handle); glfwTerminate(); #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) @@ -279,10 +292,10 @@ bool WindowShouldClose(void) // While window minimized, stop loop execution while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); - CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle); + CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle); // Reset close status for next frame - glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE); + glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); return CORE.Window.shouldClose; } @@ -325,7 +338,7 @@ void ToggleFullscreen(void) if (!CORE.Window.fullscreen) { // Store previous window position (in case we exit fullscreen) - glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); + glfwGetWindowPos(platform.handle, &CORE.Window.position.x, &CORE.Window.position.y); int monitorCount = 0; int monitorIndex = GetCurrentMonitor(); @@ -341,14 +354,14 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = false; CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } } @@ -357,7 +370,7 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = false; CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) @@ -368,9 +381,9 @@ void ToggleFullscreen(void) // Set window state: maximized, if resizable void MaximizeWindow(void) { - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) { - glfwMaximizeWindow(CORE.Window.handle); + glfwMaximizeWindow(platform.handle); CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; } } @@ -379,16 +392,16 @@ void MaximizeWindow(void) void MinimizeWindow(void) { // NOTE: Following function launches callback that sets appropriate flag! - glfwIconifyWindow(CORE.Window.handle); + glfwIconifyWindow(platform.handle); } // Set window state: not minimized/maximized void RestoreWindow(void) { - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) { // Restores the specified window if it was previously iconified (minimized) or maximized - glfwRestoreWindow(CORE.Window.handle); + glfwRestoreWindow(platform.handle); CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; } @@ -420,13 +433,13 @@ void ToggleBorderlessWindowed(void) { // Store screen position and size // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here - if (!wasOnFullscreen) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + if (!wasOnFullscreen) glfwGetWindowPos(platform.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); CORE.Window.previousScreen = CORE.Window.screen; // Set undecorated and topmost modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_TOPMOST; // Get monitor position and size @@ -437,29 +450,29 @@ void ToggleBorderlessWindowed(void) const int monitorHeight = mode->height; // Set screen position and size - glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); - glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + glfwSetWindowPos(platform.handle, monitorPosX, monitorPosY); + glfwSetWindowSize(platform.handle, monitorWidth, monitorHeight); // Refocus window - glfwFocusWindow(CORE.Window.handle); + glfwFocusWindow(platform.handle); CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; } else { // Remove topmost and undecorated modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; // Return previous screen size and position // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly - glfwSetWindowSize(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); - glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + glfwSetWindowSize(platform.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + glfwSetWindowPos(platform.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); // Refocus window - glfwFocusWindow(CORE.Window.handle); + glfwFocusWindow(platform.handle); CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; } @@ -498,21 +511,21 @@ void SetWindowState(unsigned int flags) // State change: FLAG_WINDOW_RESIZABLE if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_RESIZABLE; } // State change: FLAG_WINDOW_UNDECORATED if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; } // State change: FLAG_WINDOW_HIDDEN if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) { - glfwHideWindow(CORE.Window.handle); + glfwHideWindow(platform.handle); CORE.Window.flags |= FLAG_WINDOW_HIDDEN; } @@ -533,14 +546,14 @@ void SetWindowState(unsigned int flags) // State change: FLAG_WINDOW_UNFOCUSED if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; } // State change: FLAG_WINDOW_TOPMOST if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_TOPMOST; } @@ -567,7 +580,7 @@ void SetWindowState(unsigned int flags) // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH; } @@ -613,14 +626,14 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_RESIZABLE if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; } // State change: FLAG_WINDOW_HIDDEN if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) { - glfwShowWindow(CORE.Window.handle); + glfwShowWindow(platform.handle); CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; } @@ -639,21 +652,21 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_UNDECORATED if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; } // State change: FLAG_WINDOW_UNFOCUSED if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; } // State change: FLAG_WINDOW_TOPMOST if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; } @@ -680,7 +693,7 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH; } @@ -705,7 +718,7 @@ void SetWindowIcon(Image image) if (image.data == NULL) { // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + glfwSetWindowIcon(platform.handle, 0, NULL); } else { @@ -719,7 +732,7 @@ void SetWindowIcon(Image image) // NOTE 1: We only support one image icon // NOTE 2: The specified image data is copied before this function returns - glfwSetWindowIcon(CORE.Window.handle, 1, icon); + glfwSetWindowIcon(platform.handle, 1, icon); } else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); } @@ -734,7 +747,7 @@ void SetWindowIcons(Image *images, int count) if ((images == NULL) || (count <= 0)) { // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + glfwSetWindowIcon(platform.handle, 0, NULL); } else { @@ -754,7 +767,7 @@ void SetWindowIcons(Image *images, int count) else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); } // NOTE: Images data is copied internally before this function returns - glfwSetWindowIcon(CORE.Window.handle, valid, icons); + glfwSetWindowIcon(platform.handle, valid, icons); RL_FREE(icons); } @@ -764,13 +777,13 @@ void SetWindowIcons(Image *images, int count) void SetWindowTitle(const char *title) { CORE.Window.title = title; - glfwSetWindowTitle(CORE.Window.handle, title); + glfwSetWindowTitle(platform.handle, title); } // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - glfwSetWindowPos(CORE.Window.handle, x, y); + glfwSetWindowPos(platform.handle, x, y); } // Set monitor for the current window @@ -786,7 +799,7 @@ void SetWindowMonitor(int monitor) TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + glfwSetWindowMonitor(platform.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); } else { @@ -801,12 +814,12 @@ void SetWindowMonitor(int monitor) glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it - if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); + if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(platform.handle, monitorWorkareaX, monitorWorkareaY); else { const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); - glfwSetWindowPos(CORE.Window.handle, x, y); + glfwSetWindowPos(platform.handle, x, y); } } } @@ -822,7 +835,7 @@ void SetWindowMinSize(int width, int height) int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) @@ -834,13 +847,13 @@ void SetWindowMaxSize(int width, int height) int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } // Set window dimensions void SetWindowSize(int width, int height) { - glfwSetWindowSize(CORE.Window.handle, width, height); + glfwSetWindowSize(platform.handle, width, height); } // Set window opacity, value opacity is between 0.0 and 1.0 @@ -848,13 +861,13 @@ void SetWindowOpacity(float opacity) { if (opacity >= 1.0f) opacity = 1.0f; else if (opacity <= 0.0f) opacity = 0.0f; - glfwSetWindowOpacity(CORE.Window.handle, opacity); + glfwSetWindowOpacity(platform.handle, opacity); } // Set window focused void SetWindowFocused(void) { - glfwFocusWindow(CORE.Window.handle); + glfwFocusWindow(platform.handle); } // Get native window handle @@ -862,19 +875,19 @@ void *GetWindowHandle(void) { #if defined(_WIN32) // NOTE: Returned handle is: void *HWND (windows.h) - return glfwGetWin32Window(CORE.Window.handle); + return glfwGetWin32Window(platform.handle); #endif #if defined(__linux__) // NOTE: Returned handle is: unsigned long Window (X.h) // typedef unsigned long XID; // typedef XID Window; - //unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle); + //unsigned long id = (unsigned long)glfwGetX11Window(platform.handle); //return NULL; // TODO: Find a way to return value... cast to void *? - return (void *)CORE.Window.handle; + return (void *)platform.handle; #endif #if defined(__APPLE__) // NOTE: Returned handle is: (objc_object *) - return (void *)glfwGetCocoaWindow(CORE.Window.handle); + return (void *)glfwGetCocoaWindow(platform.handle); #endif return NULL; @@ -903,7 +916,7 @@ int GetCurrentMonitor(void) if (IsWindowFullscreen()) { // Get the handle of the monitor that the specified window is in full screen on - monitor = glfwGetWindowMonitor(CORE.Window.handle); + monitor = glfwGetWindowMonitor(platform.handle); for (int i = 0; i < monitorCount; i++) { @@ -919,7 +932,7 @@ int GetCurrentMonitor(void) int x = 0; int y = 0; - glfwGetWindowPos(CORE.Window.handle, &x, &y); + glfwGetWindowPos(platform.handle, &x, &y); for (int i = 0; i < monitorCount; i++) { @@ -1070,7 +1083,7 @@ Vector2 GetWindowPosition(void) int x = 0; int y = 0; - glfwGetWindowPos(CORE.Window.handle, &x, &y); + glfwGetWindowPos(platform.handle, &x, &y); return (Vector2){ (float)x, (float)y }; } @@ -1109,34 +1122,34 @@ Vector2 GetWindowScaleDPI(void) // Set clipboard text content void SetClipboardText(const char *text) { - glfwSetClipboardString(CORE.Window.handle, text); + glfwSetClipboardString(platform.handle, text); } // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) { - return glfwGetClipboardString(CORE.Window.handle); + return glfwGetClipboardString(platform.handle); } // Show mouse cursor void ShowCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); CORE.Input.Mouse.cursorHidden = false; } // Hides mouse cursor void HideCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); CORE.Input.Mouse.cursorHidden = true; } // Enables cursor (unlock cursor) void EnableCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); @@ -1147,7 +1160,7 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); @@ -1276,7 +1289,7 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; // NOTE: emscripten not implemented - glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); + glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } // Get mouse wheel movement Y @@ -1294,11 +1307,11 @@ float GetMouseWheelMove(void) void SetMouseCursor(int cursor) { CORE.Input.Mouse.cursor = cursor; - if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL); + if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(platform.handle, NULL); else { // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values - glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor)); + glfwSetCursor(platform.handle, glfwCreateStandardCursor(0x00036000 + cursor)); } } @@ -1331,7 +1344,7 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - glfwSwapBuffers(CORE.Window.handle); + glfwSwapBuffers(platform.handle); } // Register all input events @@ -1691,10 +1704,10 @@ static bool InitGraphicsDevice(int width, int height) // HighDPI monitors are properly considered in a following similar function: SetupViewport() SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); // NOTE: Full-screen change, not working properly... - //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { @@ -1705,16 +1718,16 @@ static bool InitGraphicsDevice(int width, int height) } // No-fullscreen window creation - CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); - if (CORE.Window.handle) + if (platform.handle) { CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; } } - if (!CORE.Window.handle) + if (!platform.handle) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); @@ -1722,23 +1735,23 @@ static bool InitGraphicsDevice(int width, int height) } // Set window callback events - glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! - glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback); - glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); - glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); + glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); + glfwSetDropCallback(platform.handle, WindowDropCallback); // Set input callback events - glfwSetKeyCallback(CORE.Window.handle, KeyCallback); - glfwSetCharCallback(CORE.Window.handle, CharCallback); - glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); - glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + glfwSetKeyCallback(platform.handle, KeyCallback); + glfwSetCharCallback(platform.handle, CharCallback); + glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); + glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(platform.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); - glfwMakeContextCurrent(CORE.Window.handle); + glfwMakeContextCurrent(platform.handle); - glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) glfwSwapInterval(0); // No V-Sync by default @@ -1760,7 +1773,7 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); #if !defined(__APPLE__) - glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight); + glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); // Screen scaling matrix is required in case desired screen area is different from display area CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); @@ -1913,7 +1926,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i } // Check the exit key to set close window - if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); #if defined(SUPPORT_SCREEN_CAPTURE) if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 2c05ab46e..8c63b63da 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -17,8 +17,10 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_DRM_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_DRM -not used- +* #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only) +* Reconfigure standard input to receive key inputs, works with SSH connection. +* WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other +* running processes orblocking the device if not restored properly. Use with care. * * DEPENDENCIES: * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) @@ -47,15 +49,93 @@ #include "rcore.h" +#include // POSIX file control definitions - open(), creat(), fcntl() +#include // POSIX standard function definitions - read(), close(), STDIN_FILENO +#include // POSIX terminal control definitions - tcgetattr(), tcsetattr() +#include // POSIX threads management (inputs reading) +#include // POSIX directory browsing + +#include // Required for: ioctl() - UNIX System call for device-specific input/output operations +#include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition +#include // Linux: Keycodes constants definition (KEY_A, ...) +#include // Linux: Joystick support library + +#include // Generic Buffer Management (native platform for EGL on DRM) +#include // Direct Rendering Manager user-level library interface +#include // Direct Rendering Manager mode setting (KMS) interface + +#include "EGL/egl.h" // Native platform windowing system interface +#include "EGL/eglext.h" // EGL extensions + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number + +#define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) +#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... + +typedef struct { + pthread_t threadId; // Event reading thread id + + int fd; // File descriptor to the device it is assigned to + int eventNum; // Number of 'event' device + Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) + int touchSlot; // Hold the touch slot number of the currently being sent multitouch block + bool isMouse; // True if device supports relative X Y movements + bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH + bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH + bool isKeyboard; // True if device has letter keycodes + bool isGamepad; // True if device has gamepad buttons +} InputEventWorker; + +typedef struct { + // Display data + int fd; // File descriptor for /dev/dri/... + drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector + drmModeCrtc *crtc; // CRT Controller + int modeIndex; // Index of the used mode of connector->modes + struct gbm_device *gbmDevice; // GBM device + struct gbm_surface *gbmSurface; // GBM surface + struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) + uint32_t prevFB; // Previous GBM framebufer (during frame swapping) + + 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 + EGLConfig config; // Graphic config + + // Input data + InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" + + // Keyboard data + int defaultKeyboardMode; // Default keyboard mode + bool eventKeyboardMode; // Keyboard in event mode + int defaultFileFlags; // Default IO file flags + struct termios defaultSettings; // Default keyboard settings + int keyboardFd; // File descriptor for the evdev keyboard + + // Mouse data + Vector2 eventWheelMove; // Registers the event mouse wheel variation + // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update + char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab + + // Gamepad data + pthread_t gamepadThreadId; // Gamepad reading thread id + int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor + +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -227,67 +307,67 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif - if (CORE.Window.prevFB) + if (platform.prevFB) { - drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); - CORE.Window.prevFB = 0; + drmModeRmFB(platform.fd, platform.prevFB); + platform.prevFB = 0; } - if (CORE.Window.prevBO) + if (platform.prevBO) { - gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); - CORE.Window.prevBO = NULL; + gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); + platform.prevBO = NULL; } - if (CORE.Window.gbmSurface) + if (platform.gbmSurface) { - gbm_surface_destroy(CORE.Window.gbmSurface); - CORE.Window.gbmSurface = NULL; + gbm_surface_destroy(platform.gbmSurface); + platform.gbmSurface = NULL; } - if (CORE.Window.gbmDevice) + if (platform.gbmDevice) { - gbm_device_destroy(CORE.Window.gbmDevice); - CORE.Window.gbmDevice = NULL; + gbm_device_destroy(platform.gbmDevice); + platform.gbmDevice = NULL; } - if (CORE.Window.crtc) + if (platform.crtc) { - if (CORE.Window.connector) + if (platform.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; + drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id, + platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode); + drmModeFreeConnector(platform.connector); + platform.connector = NULL; } - drmModeFreeCrtc(CORE.Window.crtc); - CORE.Window.crtc = NULL; + drmModeFreeCrtc(platform.crtc); + platform.crtc = NULL; } - if (CORE.Window.fd != -1) + if (platform.fd != -1) { - close(CORE.Window.fd); - CORE.Window.fd = -1; + close(platform.fd); + platform.fd = -1; } // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) + if (platform.device != EGL_NO_DISPLAY) { - if (CORE.Window.surface != EGL_NO_SURFACE) + if (platform.surface != EGL_NO_SURFACE) { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; } - if (CORE.Window.context != EGL_NO_CONTEXT) + if (platform.context != EGL_NO_CONTEXT) { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; } - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; } // Wait for mouse and gamepad threads to finish before closing @@ -297,21 +377,21 @@ void CloseWindow(void) CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called // Close the evdev keyboard - if (CORE.Input.Keyboard.fd != -1) + if (platform.keyboardFd != -1) { - close(CORE.Input.Keyboard.fd); - CORE.Input.Keyboard.fd = -1; + close(platform.keyboardFd); + platform.keyboardFd = -1; } - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].threadId) + if (platform.eventWorker[i].threadId) { - pthread_join(CORE.Input.eventWorker[i].threadId, NULL); + pthread_join(platform.eventWorker[i].threadId, NULL); } } - if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); + if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -524,9 +604,9 @@ int GetMonitorRefreshRate(int monitor) { int refresh = 0; - if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0)) + if ((platform.connector) && (platform.modeIndex >= 0)) { - refresh = CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh; + refresh = platform.connector->modes[platform.modeIndex].vrefresh; } return refresh; @@ -659,7 +739,7 @@ const char *GetGamepadName(int gamepad) if (CORE.Input.Gamepad.ready[gamepad]) { - ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); + ioctl(platform.gamepadStreamFd[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); name = CORE.Input.Gamepad.name[gamepad]; } @@ -670,7 +750,7 @@ const char *GetGamepadName(int gamepad) int GetGamepadAxisCount(int gamepad) { int axisCount = 0; - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); + if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; return CORE.Input.Gamepad.axisCount; @@ -756,31 +836,31 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - eglSwapBuffers(CORE.Window.device, CORE.Window.surface); + eglSwapBuffers(platform.device, platform.surface); - if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); - struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface); + struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); 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); + 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); if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); - 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]); + result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]); if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); - if (CORE.Window.prevFB) + if (platform.prevFB) { - result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); + result = drmModeRmFB(platform.fd, platform.prevFB); if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); } - CORE.Window.prevFB = fb; + platform.prevFB = fb; - if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); + if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); - CORE.Window.prevBO = bo; + platform.prevBO = bo; } // Register all input events @@ -814,12 +894,12 @@ void PollInputEvents(void) // Register previous mouse states CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; - CORE.Input.Mouse.eventWheelMove = (Vector2){ 0.0f, 0.0f }; + CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove; + platform.eventWheelMove = (Vector2){ 0.0f, 0.0f }; for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) { CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; - CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i]; + CORE.Input.Mouse.currentButtonState[i] = platform.currentButtonStateEvdev[i]; } // Register gamepads buttons events @@ -844,7 +924,7 @@ void PollInputEvents(void) // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console - if (!CORE.Input.Keyboard.evtMode) ProcessKeyboard(); + if (!platform.eventKeyboardMode) ProcessKeyboard(); // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread() // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() @@ -877,41 +957,41 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - 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; + platform.fd = -1; + platform.connector = NULL; + platform.modeIndex = -1; + platform.crtc = NULL; + platform.gbmDevice = NULL; + platform.gbmSurface = NULL; + platform.prevBO = NULL; + platform.prevFB = 0; #if defined(DEFAULT_GRAPHIC_DEVICE_DRM) - CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); + platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); #else TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card"); - CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) + platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) - if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); - CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded + platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded } - if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { 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) + platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) } #endif - if (CORE.Window.fd == -1) + if (platform.fd == -1) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); return false; } - drmModeRes *res = drmModeGetResources(CORE.Window.fd); + drmModeRes *res = drmModeGetResources(platform.fd); if (!res) { TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); @@ -924,13 +1004,13 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i); - drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]); + drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]); TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) { TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); - CORE.Window.connector = con; + platform.connector = con; break; } else @@ -940,14 +1020,14 @@ static bool InitGraphicsDevice(int width, int height) } } - if (!CORE.Window.connector) + if (!platform.connector) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); drmModeFreeResources(res); return false; } - drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id); + drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); if (!enc) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); @@ -955,8 +1035,8 @@ static bool InitGraphicsDevice(int width, int height) return false; } - CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id); - if (!CORE.Window.crtc) + platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id); + if (!platform.crtc) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); drmModeFreeEncoder(enc); @@ -969,9 +1049,9 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode..."); - CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode); + platform.modeIndex = FindMatchingConnectorMode(platform.connector, &platform.crtc->mode); - if (CORE.Window.modeIndex < 0) + if (platform.modeIndex < 0) { TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); drmModeFreeEncoder(enc); @@ -987,19 +1067,19 @@ static bool InitGraphicsDevice(int width, int height) 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); + platform.modeIndex = FindExactConnectorMode(platform.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 (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.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 (platform.modeIndex < 0) platform.modeIndex = FindExactConnectorMode(platform.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 (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); // If nothing found, there is no suitable mode - if (CORE.Window.modeIndex < 0) + if (platform.modeIndex < 0) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); drmModeFreeEncoder(enc); @@ -1007,13 +1087,13 @@ static bool InitGraphicsDevice(int width, int height) 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; + CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay; + CORE.Window.display.height = platform.connector->modes[platform.modeIndex].vdisplay; - TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector 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); + TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name, + platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, + (platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p', + platform.connector->modes[platform.modeIndex].vrefresh); // Use the width and height of the surface for render CORE.Window.render.width = CORE.Window.screen.width; @@ -1025,16 +1105,16 @@ static bool InitGraphicsDevice(int width, int height) drmModeFreeResources(res); res = NULL; - CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd); - if (!CORE.Window.gbmDevice) + platform.gbmDevice = gbm_create_device(platform.fd); + if (!platform.gbmDevice) { TRACELOG(LOG_WARNING, "DISPLAY: 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) + platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay, + platform.connector->modes[platform.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (!platform.gbmSurface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface"); return false; @@ -1073,22 +1153,22 @@ static bool InitGraphicsDevice(int width, int height) EGLint numConfigs = 0; // Get an EGL device connection - CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice); - if (CORE.Window.device == EGL_NO_DISPLAY) + platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice); + if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); return false; } // Initialize the EGL device connection - if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) + if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE) { // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); return false; } - if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs)) + if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); return false; @@ -1104,7 +1184,7 @@ static bool InitGraphicsDevice(int width, int height) } EGLint matchingNumConfigs = 0; - if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) + if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); free(configs); @@ -1118,7 +1198,7 @@ static bool InitGraphicsDevice(int width, int height) for (EGLint i = 0; i < matchingNumConfigs; ++i) { EGLint id = 0; - if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id)) + if (!eglGetConfigAttrib(platform.device, configs[i], EGL_NATIVE_VISUAL_ID, &id)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError()); continue; @@ -1127,7 +1207,7 @@ static bool InitGraphicsDevice(int width, int height) if (GBM_FORMAT_ARGB8888 == id) { TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i); - CORE.Window.config = configs[i]; + platform.config = configs[i]; found = 1; break; } @@ -1145,8 +1225,8 @@ static bool InitGraphicsDevice(int width, int height) eglBindAPI(EGL_OPENGL_ES_API); // Create an EGL rendering context - CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); - if (CORE.Window.context == EGL_NO_CONTEXT) + platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs); + if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); return false; @@ -1154,8 +1234,8 @@ static bool InitGraphicsDevice(int width, int height) // Create an EGL window surface //--------------------------------------------------------------------------------- - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); - if (EGL_NO_SURFACE == CORE.Window.surface) + platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL); + if (EGL_NO_SURFACE == platform.surface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); return false; @@ -1169,9 +1249,9 @@ static bool InitGraphicsDevice(int width, int height) SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); // There must be at least one frame displayed before the buffers are swapped - //eglSwapInterval(CORE.Window.device, 1); + //eglSwapInterval(platform.device, 1); - if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); return false; @@ -1214,11 +1294,11 @@ static void InitKeyboard(void) // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE // Save terminal keyboard settings - tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings); + tcgetattr(STDIN_FILENO, &platform.defaultSettings); // Reconfigure terminal with new settings struct termios keyboardNewSettings = { 0 }; - keyboardNewSettings = CORE.Input.Keyboard.defaultSettings; + keyboardNewSettings = platform.defaultSettings; // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing // NOTE: ISIG controls if ^C and ^Z generate break signals or not @@ -1231,11 +1311,11 @@ static void InitKeyboard(void) tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings); // Save old keyboard mode to restore it at the end - CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified + platform.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags + fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno) - int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode); + int result = ioctl(STDIN_FILENO, KDGKBMODE, &platform.defaultKeyboardMode); // In case of failure, it could mean a remote keyboard is used (SSH) if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used"); @@ -1257,11 +1337,11 @@ static void InitKeyboard(void) static void RestoreKeyboard(void) { // Reset to default keyboard settings - tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings); + tcsetattr(STDIN_FILENO, TCSANOW, &platform.defaultSettings); // Reconfigure keyboard to default mode - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags); - ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode); + fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags); + ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode); } #if defined(SUPPORT_SSH_KEYBOARD_RPI) @@ -1389,7 +1469,7 @@ static void InitEvdevInput(void) struct dirent *entity = NULL; // Initialise keyboard file descriptor - CORE.Input.Keyboard.fd = -1; + platform.keyboardFd = -1; // Reset variables for (int i = 0; i < MAX_TOUCH_POINTS; ++i) @@ -1451,9 +1531,9 @@ static void ConfigureEvdevDevice(char *device) // Open the device and allocate worker //------------------------------------------------------------------------------------------------------- // Find a free spot in the workers array - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].threadId == 0) + if (platform.eventWorker[i].threadId == 0) { freeWorkerId = i; break; @@ -1463,7 +1543,7 @@ static void ConfigureEvdevDevice(char *device) // Select the free worker from array if (freeWorkerId >= 0) { - worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker + worker = &(platform.eventWorker[freeWorkerId]); // Grab a pointer to the worker memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker } else @@ -1574,13 +1654,13 @@ static void ConfigureEvdevDevice(char *device) // Decide what to do with the device //------------------------------------------------------------------------------------------------------- - if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1)) + if (worker->isKeyboard && (platform.keyboardFd == -1)) { // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate // threads so that they don't drop events when the frame rate is slow. TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); - CORE.Input.Keyboard.fd = worker->fd; + platform.keyboardFd = worker->fd; } else if (worker->isTouch || worker->isMouse) { @@ -1604,21 +1684,21 @@ static void ConfigureEvdevDevice(char *device) // Find touchscreen with the highest index int maxTouchNumber = -1; - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; + if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = platform.eventWorker[i].eventNum; } // Find touchscreens with lower indexes - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) + if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum < maxTouchNumber)) { - if (CORE.Input.eventWorker[i].threadId != 0) + if (platform.eventWorker[i].threadId != 0) { TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i); - pthread_cancel(CORE.Input.eventWorker[i].threadId); - close(CORE.Input.eventWorker[i].fd); + pthread_cancel(platform.eventWorker[i].threadId); + close(platform.eventWorker[i].fd); } } } @@ -1652,7 +1732,7 @@ static void PollKeyboardEvents(void) 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 }; - int fd = CORE.Input.Keyboard.fd; + int fd = platform.keyboardFd; if (fd == -1) return; struct input_event event = { 0 }; @@ -1666,7 +1746,7 @@ static void PollKeyboardEvents(void) { #if defined(SUPPORT_SSH_KEYBOARD_RPI) // Change keyboard mode to events - CORE.Input.Keyboard.evtMode = true; + platform.eventKeyboardMode = true; #endif // Keyboard button parsing if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 @@ -1739,7 +1819,7 @@ static void *EventThread(void *arg) gestureUpdate = true; } - if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value; + if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value; } // Absolute movement parsing @@ -1790,11 +1870,11 @@ static void *EventThread(void *arg) // Touchscreen tap if (event.code == ABS_PRESSURE) { - int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; + int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; if (!event.value && previousMouseLeftButtonState) { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; + platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; touchAction = 0; // TOUCH_ACTION_UP gestureUpdate = true; @@ -1802,7 +1882,7 @@ static void *EventThread(void *arg) if (event.value && !previousMouseLeftButtonState) { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; + platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; touchAction = 1; // TOUCH_ACTION_DOWN gestureUpdate = true; @@ -1817,19 +1897,19 @@ static void *EventThread(void *arg) // Mouse button parsing if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; + platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN else touchAction = 0; // TOUCH_ACTION_UP gestureUpdate = true; } - if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; - if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; - if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; - if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; - if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; - if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value; + if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; + if (event.code == BTN_MIDDLE) platform.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; + if (event.code == BTN_SIDE) platform.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; + if (event.code == BTN_EXTRA) platform.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; + if (event.code == BTN_FORWARD) platform.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; + if (event.code == BTN_BACK) platform.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value; } // Screen confinement @@ -1885,7 +1965,7 @@ static void InitGamepad(void) { sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i); - if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) + if ((platform.gamepadStreamFd[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) { // NOTE: Only show message for first gamepad if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available"); @@ -1897,7 +1977,7 @@ static void InitGamepad(void) // NOTE: Only create one thread if (i == 0) { - int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL); + int error = pthread_create(&platform.gamepadThreadId, NULL, &GamepadThread, NULL); if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); @@ -1927,7 +2007,7 @@ static void *GamepadThread(void *arg) { for (int i = 0; i < MAX_GAMEPADS; i++) { - if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) + if (read(platform.gamepadStreamFd[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) { gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events @@ -1977,7 +2057,7 @@ static int FindMatchingConnectorMode(const drmModeConnector *connector, const dr TRACELOG(LOG_TRACE, "DISPLAY: DRM 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])) return i; + if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i; } return -1; @@ -1992,9 +2072,9 @@ static int FindExactConnectorMode(const drmModeConnector *connector, uint width, if (NULL == connector) return -1; - for (int i = 0; i < CORE.Window.connector->count_modes; i++) + for (int i = 0; i < platform.connector->count_modes; i++) { - const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i]; + const drmModeModeInfo *const mode = &platform.connector->modes[i]; 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"); @@ -2015,9 +2095,9 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt if (NULL == connector) return -1; int nearestIndex = -1; - for (int i = 0; i < CORE.Window.connector->count_modes; i++) + for (int i = 0; i < platform.connector->count_modes; i++) { - const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i]; + const drmModeModeInfo *const mode = &platform.connector->modes[i]; 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"); @@ -2044,9 +2124,9 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt const int heightDiff = abs(mode->vdisplay - height); const int fpsDiff = abs(mode->vrefresh - fps); - const int nearestWidthDiff = abs(CORE.Window.connector->modes[nearestIndex].hdisplay - width); - const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height); - const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps); + const int nearestWidthDiff = abs(platform.connector->modes[nearestIndex].hdisplay - width); + const int nearestHeightDiff = abs(platform.connector->modes[nearestIndex].vdisplay - height); + const int nearestFpsDiff = abs(platform.connector->modes[nearestIndex].vrefresh - fps); if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) { nearestIndex = i; diff --git a/src/rcore_web.c b/src/rcore_web.c index baafd60d6..fa296b90b 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -47,23 +47,42 @@ #include "rcore.h" -#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) -// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) -#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management +#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) +// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) +#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management + +#include // Emscripten functionality for C +#include // Emscripten HTML5 library + #include // Required for: timespec, nanosleep(), select() - POSIX -#include // Emscripten functionality for C -#include // Emscripten HTML5 library +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// TODO: HACK: Added flag if not provided by GLFW when using external library +// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev +#if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +#endif + +#if (_POSIX_C_SOURCE < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... +typedef struct { + GLFWwindow *handle; // GLFW window handle (graphic device) +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -263,7 +282,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - glfwDestroyWindow(CORE.Window.handle); + glfwDestroyWindow(platform.handle); glfwTerminate(); #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) @@ -481,7 +500,7 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - glfwSetWindowSize(CORE.Window.handle, width, height); + glfwSetWindowSize(platform.handle, width, height); } // Set window opacity, value opacity is between 0.0 and 1.0 @@ -759,7 +778,7 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; // NOTE: emscripten not implemented - glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); + glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } // Get mouse wheel movement Y @@ -806,7 +825,7 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - glfwSwapBuffers(CORE.Window.handle); + glfwSwapBuffers(platform.handle); } // Register all input events @@ -1110,24 +1129,24 @@ static bool InitGraphicsDevice(int width, int height) // HighDPI monitors are properly considered in a following similar function: SetupViewport() SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); // NOTE: Full-screen change, not working properly... - // glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + // glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { // No-fullscreen window creation - CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); - if (CORE.Window.handle) + if (platform.handle) { CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; } } - if (!CORE.Window.handle) + if (!platform.handle) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); @@ -1138,20 +1157,20 @@ static bool InitGraphicsDevice(int width, int height) emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); // Set window callback events - glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! - glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); - glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); + glfwSetDropCallback(platform.handle, WindowDropCallback); // Set input callback events - glfwSetKeyCallback(CORE.Window.handle, KeyCallback); - glfwSetCharCallback(CORE.Window.handle, CharCallback); - glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); - glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + glfwSetKeyCallback(platform.handle, KeyCallback); + glfwSetCharCallback(platform.handle, CharCallback); + glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); + glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(platform.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); - glfwMakeContextCurrent(CORE.Window.handle); + glfwMakeContextCurrent(platform.handle); // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need @@ -1293,7 +1312,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i } // Check the exit key to set close window - if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); #if defined(SUPPORT_SCREEN_CAPTURE) if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) diff --git a/src/utils.h b/src/utils.h index ff8246a7b..6ec0f7f1b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,7 +32,7 @@ #include // Required for: AAssetManager #endif -#if defined(SUPPORT_TRACELOG) +#if defined(SUPPORT_TRACELOG) && !defined(TRACELOG) #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) #if defined(SUPPORT_TRACELOG_DEBUG) From df8d3a5afb6bec908ad0e720a1cbb2b444cb951d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 00:47:22 +0200 Subject: [PATCH 0004/1037] REVIEWED: Some warnings #3313 --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index f28aff74f..85a84f590 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1413,10 +1413,10 @@ const char *GetDirectoryPath(const char *filePath) else { // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' - unsigned char *dirPathPtr = dirPath; + char *dirPathPtr = dirPath; if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2; // Skip drive letter, "C:" memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1)); - dirPath[strlen(filePath) - strlen(lastSlash) + ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0] = '\0'; // Add '\0' manually + dirPath[strlen(filePath) - strlen(lastSlash) + (((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0)] = '\0'; // Add '\0' manually } } From a0b30b0363cfbadb359db87ffd8fa80748485980 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 01:02:19 +0200 Subject: [PATCH 0005/1037] REVIEWED: `SetupViewport()` macOS #3313 --- src/rcore.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 85a84f590..3a8820e38 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2162,9 +2162,10 @@ void SetupViewport(int width, int height) // NOTE: We consider render size (scaled) and offset in case black bars are required and // render area does not match full display area (this situation is only applicable on fullscreen mode) #if defined(__APPLE__) - float xScale = 1.0f, yScale = 1.0f; - glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); - rlViewport(CORE.Window.renderOffset.x/2*xScale, CORE.Window.renderOffset.y/2*yScale, (CORE.Window.render.width)*xScale, (CORE.Window.render.height)*yScale); + //float xScale = 1.0f, yScale = 1.0f; + //glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); + Vector2 scale = GetWindowScaleDPI(); + rlViewport(CORE.Window.renderOffset.x/2*scale.x, CORE.Window.renderOffset.y/2*scale.y, (CORE.Window.render.width)*scale.x, (CORE.Window.render.height)*scale.y); #else rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width, CORE.Window.render.height); #endif From 5ed7717f0d9e524b72f1bddc6fcd3e2e5f32e217 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 01:21:46 +0200 Subject: [PATCH 0006/1037] REVIEWED: `WaitTime()`, added validation #3377 --- src/rcore.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 3a8820e38..69b5e1a83 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2290,6 +2290,8 @@ void InitTimer(void) // Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32! void WaitTime(double seconds) { + if (seconds < 0) return; + #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) double destinationTime = GetTime() + seconds; #endif From 682992e868ffd97fa25d432e15ec209a161c26d9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 09:45:57 +0200 Subject: [PATCH 0007/1037] REVIEWED: Reorganize functions `TakeScreenshot()` moved to `rcore.c` --- src/rcore.c | 23 ++++++++++++ src/rcore_android.c | 44 ++++++++-------------- src/rcore_desktop.c | 42 ++++++--------------- src/rcore_drm.c | 90 ++++++++++++++++++--------------------------- src/rcore_web.c | 46 ++++++----------------- 5 files changed, 97 insertions(+), 148 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 69b5e1a83..a3075aac5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2329,6 +2329,29 @@ void WaitTime(double seconds) #endif } +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[512] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + // Scan all files and directories in a base path // WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths diff --git a/src/rcore_android.c b/src/rcore_android.c index ad7a905c1..a7c8f3307 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -133,6 +133,10 @@ struct android_app *GetAndroidApp(void) return platform.app; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + // Initialize window and OpenGL context // NOTE: data parameter could be used to pass any kind of required data to the initialization void InitWindow(int width, int height, const char *title) @@ -563,6 +567,16 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(platform.device, platform.surface); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -576,29 +590,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_ANDROID - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Open URL with default system browser (if available) // NOTE: This function is only safe to use if you control the URL given. // A user could craft a malicious string performing another action. @@ -726,12 +717,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - eglSwapBuffers(platform.device, platform.surface); -} - // Register all input events void PollInputEvents(void) { @@ -787,6 +772,7 @@ void PollInputEvents(void) } } + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 33a4aab0e..dcd71ef18 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -137,7 +137,7 @@ static void CursorEnterCallback(GLFWwindow *window, int enter); // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Initialize window and OpenGL context @@ -1168,6 +1168,16 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(platform.handle); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -1175,30 +1185,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -// WARNING: This function requires [rtextures] module functionality -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Open URL with default system browser (if available) // NOTE: This function is only safe to use if you control the URL given. // A user could craft a malicious string performing another action. @@ -1341,12 +1327,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - glfwSwapBuffers(platform.handle); -} - // Register all input events void PollInputEvents(void) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 8c63b63da..aadf0f475 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -166,7 +166,7 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Initialize window and OpenGL context @@ -675,6 +675,40 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(platform.device, platform.surface); + + if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + + struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); + if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); + + uint32_t fb = 0; + 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); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); + + result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); + + if (platform.prevFB) + { + result = drmModeRmFB(platform.fd, platform.prevFB); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); + } + + platform.prevFB = fb; + + if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); + + platform.prevBO = bo; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -688,29 +722,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_WEB - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Open URL with default system browser (if available) // NOTE: This function is only safe to use if you control the URL given. // A user could craft a malicious string performing another action. @@ -833,36 +844,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - eglSwapBuffers(platform.device, platform.surface); - - if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); - - struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); - if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); - - uint32_t fb = 0; - 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); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); - - result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); - - if (platform.prevFB) - { - result = drmModeRmFB(platform.fd, platform.prevFB); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); - } - - platform.prevFB = fb; - - if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); - - platform.prevBO = bo; -} - // Register all input events void PollInputEvents(void) { @@ -931,6 +912,7 @@ void PollInputEvents(void) #endif } + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/rcore_web.c b/src/rcore_web.c index fa296b90b..249f0db4c 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -122,7 +122,7 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Initialize window and OpenGL context @@ -664,6 +664,16 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(platform.handle); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -671,33 +681,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_WEB - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Open URL with default system browser (if available) // NOTE: This function is only safe to use if you control the URL given. // A user could craft a malicious string performing another action. @@ -822,12 +805,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - glfwSwapBuffers(platform.handle); -} - // Register all input events void PollInputEvents(void) { @@ -944,6 +921,7 @@ void PollInputEvents(void) } } + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- From ea9de852bdc6b6867fdf2805e6c5caf476b61bde Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 09:46:57 +0200 Subject: [PATCH 0008/1037] ADDED: Custom platform template! #3313 --- src/rcore_custom.c | 789 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 789 insertions(+) create mode 100644 src/rcore_custom.c diff --git a/src/rcore_custom.c b/src/rcore_custom.c new file mode 100644 index 000000000..f20996268 --- /dev/null +++ b/src/rcore_custom.c @@ -0,0 +1,789 @@ +/********************************************************************************************** +* +* rcore_ - 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_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_ -not used- +* +* DEPENDENCIES: +* - Dependency 01 +* - Dependency 02 +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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 "rcore.h" + +// TODO: Include the platform specific libraries + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { + // TODO: Define the platform specific variables required + + // Display data + 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 + EGLConfig config; // Graphic config +} PlatformData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + + // TODO: Initialize window/display system + + // TODO: Initialize input events system + + // TODO: Initialize assets manager + + // TODO: Initialize base path for storage + //CORE.Storage.basePath = platform.app->activity->internalDataPath; + + TRACELOG(LOG_INFO, "PLATFORM: Application initialized successfully"); +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + + // TODO: Close surface, context and display + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return platform.appEnabled; +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_CUSTOM"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_CUSTOM"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_CUSTOM"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_CUSTOM"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_CUSTOM"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_CUSTOM"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_CUSTOM"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_CUSTOM"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_CUSTOM"); +} + +// 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 PLATFORM_CUSTOM"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_CUSTOM"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_CUSTOM"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_CUSTOM"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_CUSTOM"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_CUSTOM"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_CUSTOM"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_CUSTOM"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_CUSTOM"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_CUSTOM"); + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_CUSTOM"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_CUSTOM"); + return NULL; +} + +// 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.device, platform.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; +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code on PLATFORM_CUSTOM + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + JNIEnv *env = NULL; + JavaVM *vm = platform.app->activity->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + + jstring urlString = (*env)->NewStringUTF(env, url); + jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); + jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); + jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); + + jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); + jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); + jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); + jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); + jobject intent = (*env)->AllocObject(env, intentClass); + + (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); + jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); + jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); + (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent); + + (*vm)->DetachCurrentThread(vm); + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +void SetExitKey(int key) +{ + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_CUSTOM"); +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_CUSTOM"); + return NULL; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + return GetTouchPosition(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; +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_CUSTOM"); + return 0.0f; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_CUSTOM"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + 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); + + return position; +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_CUSTOM the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Register previous keys states + // NOTE: Android supports up to 260 keys + for (int i = 0; i < 260; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // TODO: Poll input events for current plaform +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + EGLint samples = 0; + EGLint sampleBuffer = 0; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + samples = 4; + sampleBuffer = 1; + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + } + + const EGLint framebufferAttribs[] = + { + EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support + 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_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 + EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA + EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) + EGL_NONE + }; + + const EGLint contextAttribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint numConfigs = 0; + + // Get an EGL device connection + platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (platform.device == EGL_NO_DISPLAY) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Initialize the EGL device connection + if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE) + { + // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Get an appropriate EGL framebuffer configuration + eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs); + + // Set rendering API + eglBindAPI(EGL_OPENGL_ES_API); + + // Create an EGL rendering context + platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs); + if (platform.context == EGL_NO_CONTEXT) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); + return false; + } + + // Create an EGL window surface + //--------------------------------------------------------------------------------- + EGLint displayFormat = 0; + + // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() + // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID + eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + + // 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); + + ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size + + platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL); + + // There must be at least one frame displayed before the buffers are swapped + //eglSwapInterval(platform.device, 1); + + if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + return false; + } + else + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + CORE.Window.ready = true; + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// EOF From d309b1eaa7712dc0d15431bfb6efc0508e6ecbc8 Mon Sep 17 00:00:00 2001 From: Masoud Naservand Date: Mon, 9 Oct 2023 11:17:54 +0330 Subject: [PATCH 0009/1037] Call nsvgDeleteRasterizer() on created rasterizer (#3392) the `NSVGrasterizer *rast` needs to be passed to nsvgDeleteRasterizer() when we are done with it. --- src/rtextures.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index fa8d91ea4..dc7f4af05 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -384,6 +384,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) // Free used memory nsvgDelete(svgImage); + nsvgDeleteRasterizer(rast); } if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData); @@ -555,6 +556,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; nsvgDelete(svgImage); + nsvgDeleteRasterizer(rast); } } #endif From 7ab911b9a4fb0ba3798bb8f117e69475a4fb89bc Mon Sep 17 00:00:00 2001 From: "Dennis E. Hamilton" Date: Mon, 9 Oct 2023 00:49:58 -0700 Subject: [PATCH 0010/1037] Ensure m3d faces in non-decreasing materialid sequence (#3385) This modification replaces the expensive qsort protection with an insertion sort that is near-instantaneous in the expected ordered case. --- src/rmodels.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 490eac555..77c89b6e9 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5614,19 +5614,26 @@ static Model LoadM3D(const char *fileName) // We always need a default material, so we add +1 model.materialCount++; - // WARNING: Sorting is not needed, valid M3D model files should already be sorted - // faces should already by grouped by material - // Just keeping the sorting function for reference (Check PR #3363) - /* - static int m3d_compare_faces(const void *a, const void *b) + // Faces must be in non-decreasing materialid order + // Verify that quickly, sorting them otherwise. + for (i = 1; i < m3d->numface; i++) { - m3df_t *fa = (m3df_t *)a; - m3df_t *fb = (m3df_t *)b; - return (fa->materialid - fb->materialid); + if ( m3d->face[i-1].materialid <= m3d->face[i].materialid ) + continue; + + // face[i-1] > face[i]. slide face[i] lower. + m3df_t slider = m3d->face[i]; + j = i-1; + + do + { // face[j] > slider, face[j+1] is svailable vacant gap. + m3d->face[j+1] = m3d->face[j]; + j = j-1; + } + while (j >= 0 && m3d->face[j].materialid > slider.materialid); + + m3d->face[j+1] = slider; } - - qsort(m3d->face, m3d->numface, sizeof(m3df_t), m3d_compare_faces); - */ model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); From 33c84b3c0092982aa3472e94a766f6bf711a31b9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 09:53:52 +0200 Subject: [PATCH 0011/1037] Update rmodels.c --- src/rmodels.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 77c89b6e9..0809fce80 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5614,12 +5614,13 @@ static Model LoadM3D(const char *fileName) // We always need a default material, so we add +1 model.materialCount++; - // Faces must be in non-decreasing materialid order - // Verify that quickly, sorting them otherwise. + // Faces must be in non-decreasing materialid order. Verify that quickly, sorting them otherwise. + // WARNING: Sorting is not needed, valid M3D model files should already be sorted + // Just keeping the sorting function for reference (Check PR #3363 #3385) + /* for (i = 1; i < m3d->numface; i++) { - if ( m3d->face[i-1].materialid <= m3d->face[i].materialid ) - continue; + if (m3d->face[i-1].materialid <= m3d->face[i].materialid) continue; // face[i-1] > face[i]. slide face[i] lower. m3df_t slider = m3d->face[i]; @@ -5634,6 +5635,7 @@ static Model LoadM3D(const char *fileName) m3d->face[j+1] = slider; } + */ model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); From dfb0326d00c65e145472464b5ebe74802fe630cc Mon Sep 17 00:00:00 2001 From: SuperUserNameMan Date: Mon, 9 Oct 2023 09:54:43 +0200 Subject: [PATCH 0012/1037] Update rcore.c (#3326) --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index a3075aac5..29881198a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -762,7 +762,7 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) { VrStereoConfig config = { 0 }; - if ((rlGetVersion() == RL_OPENGL_33) || (rlGetVersion() >= RL_OPENGL_ES_20)) + if (rlGetVersion() != RL_OPENGL_11) { // Compute aspect ratio float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution; From f86f4159e6eb4ed84cca6147c2f1e76ff2524c19 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 10:51:37 +0200 Subject: [PATCH 0013/1037] Avoid references to `PLATFORM_` flags #3313 --- src/rcore_android.c | 76 ++++++++++++++++++++++----------------------- src/rcore_custom.c | 74 +++++++++++++++++++++---------------------- src/rcore_desktop.c | 6 ++-- src/rcore_drm.c | 60 +++++++++++++++++------------------ src/rcore_web.c | 62 ++++++++++++++++++------------------ 5 files changed, 135 insertions(+), 143 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index a7c8f3307..bd0674b29 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -17,8 +17,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_ANDROID_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_ANDROID -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * Android NDK - Provides C API to access Android functionality @@ -338,55 +338,55 @@ bool IsWindowResized(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -398,13 +398,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -424,116 +424,116 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + 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 PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } @@ -597,7 +597,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (partially) avoid malicious code on PLATFORM_ANDROID + // Security check to (partially) avoid malicious code if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { @@ -632,13 +632,13 @@ void OpenURL(const char *url) // Set a custom key to exit program void SetExitKey(int key) { - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); } // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); return NULL; } @@ -651,7 +651,7 @@ int GetGamepadAxisCount(int gamepad) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } @@ -683,14 +683,14 @@ void SetMousePosition(int x, int y) // Get mouse wheel movement Y float GetMouseWheelMove(void) { - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); return 0.0f; } // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get touch position X for touch point 0 (relative to screen size) @@ -740,8 +740,6 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_ANDROID the mouse position and not filled again until a move-event, - // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; // Register previous keys states @@ -1222,7 +1220,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) int32_t action = AMotionEvent_getAction(event); unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID +#if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent = { 0 }; gestureEvent.pointCount = CORE.Input.Touch.pointCount; diff --git a/src/rcore_custom.c b/src/rcore_custom.c index f20996268..2d35a3b4f 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -17,8 +17,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_ -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * - Dependency 01 @@ -219,55 +219,55 @@ bool IsWindowResized(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -279,13 +279,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -305,116 +305,116 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + 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 PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } @@ -478,7 +478,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (partially) avoid malicious code on PLATFORM_CUSTOM + // Security check to (partially) avoid malicious code on target platform if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { @@ -513,13 +513,13 @@ void OpenURL(const char *url) // Set a custom key to exit program void SetExitKey(int key) { - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); } // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); return NULL; } @@ -532,7 +532,7 @@ int GetGamepadAxisCount(int gamepad) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } @@ -564,14 +564,14 @@ void SetMousePosition(int x, int y) // Get mouse wheel movement Y float GetMouseWheelMove(void) { - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); return 0.0f; } // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get touch position X for touch point 0 (relative to screen size) @@ -622,7 +622,7 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_CUSTOM the mouse position and not filled again until a move-event, + // TODO: It resets on target platform the mouse position and not filled again until a move-event, // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index dcd71ef18..2c9872a34 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -20,8 +20,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_DESKTOP_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_DESKTOP -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * rglfw - Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) @@ -1368,8 +1368,6 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, - // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; // Check if gamepads are ready diff --git a/src/rcore_drm.c b/src/rcore_drm.c index aadf0f475..50e45c4ae 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -442,55 +442,55 @@ bool IsWindowResized(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -502,13 +502,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -528,74 +528,74 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); return 0; } @@ -615,7 +615,7 @@ int GetMonitorRefreshRate(int monitor) // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); return ""; } @@ -634,14 +634,14 @@ Vector2 GetWindowScaleDPI(void) // Set clipboard text content void SetClipboardText(const char *text) { - TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_DRM"); + 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 PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } @@ -729,7 +729,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - TRACELOG(LOG_WARNING, "OpenURL() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform"); } //---------------------------------------------------------------------------------- @@ -770,7 +770,7 @@ int GetGamepadAxisCount(int gamepad) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } @@ -818,7 +818,7 @@ float GetMouseWheelMove(void) // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get touch position X for touch point 0 (relative to screen size) @@ -897,8 +897,6 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, - // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; #if defined(SUPPORT_SSH_KEYBOARD_RPI) @@ -1911,7 +1909,7 @@ static void *EventThread(void *arg) if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++; } -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM +#if defined(SUPPORT_GESTURES_SYSTEM) if (gestureUpdate) { GestureEvent gestureEvent = { 0 }; diff --git a/src/rcore_web.c b/src/rcore_web.c index 249f0db4c..111666045 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -16,8 +16,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_WEB_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_WEB -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * emscripten - Allow interaction between browser API and C @@ -413,49 +413,49 @@ void ToggleFullscreen(void) // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); } // Set icon for window, multiple images void SetWindowIcons(Image *images, int count) { - TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -468,13 +468,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -506,96 +506,96 @@ void SetWindowSize(int width, int height) // Set window opacity, value opacity is between 0.0 and 1.0 void SetWindowOpacity(float opacity) { - TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + 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 PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); return (Vector2){ 1.0f, 1.0f }; } @@ -688,7 +688,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (partially) avoid malicious code on PLATFORM_WEB + // Security check to (partially) avoid malicious code on target platform if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else emscripten_run_script(TextFormat("window.open('%s', '_blank')", url)); } @@ -745,9 +745,7 @@ Vector2 GetMousePosition(void) { Vector2 position = { 0 }; - // TODO: Review touch position - - // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned + // NOTE: On canvas scaling, mouse position is proportionally returned position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; @@ -846,7 +844,7 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, + // TODO: It resets on target platform the mouse position and not filled again until a move-event, // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; @@ -1567,7 +1565,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0; } -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB +#if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent = {0}; gestureEvent.pointCount = CORE.Input.Touch.pointCount; From b55cf40b910a428cba7e69ea9133d555a63b818b Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 11:07:02 +0200 Subject: [PATCH 0014/1037] Format tweaks --- src/rcore.h | 10 +++++----- src/rcore_android.c | 19 ++++++++++++------- src/rcore_custom.c | 25 ++++++++++++------------- src/rcore_desktop.c | 21 ++++++++++++--------- src/rcore_drm.c | 31 +++++++++++++++++-------------- src/rcore_web.c | 17 ++++++++--------- src/rmodels.c | 8 ++++---- 7 files changed, 70 insertions(+), 61 deletions(-) diff --git a/src/rcore.h b/src/rcore.h index 132c8e007..7fb35915a 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -133,18 +133,18 @@ typedef struct CoreData { char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings - + } Window; struct { const char *basePath; // Base path for data storage - + } Storage; struct { struct { int exitKey; // Default exit key char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state - + // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. @@ -177,7 +177,7 @@ typedef struct CoreData { Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state - + } Touch; struct { int lastButtonPressed; // Register last gamepad button pressed @@ -199,7 +199,7 @@ typedef struct CoreData { double target; // Desired time for one frame, if 0 not applied unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) unsigned int frameCounter; // Frame counter - + } Time; } CoreData; diff --git a/src/rcore_android.c b/src/rcore_android.c index bd0674b29..a3db7e5fa 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_android - Functions to manage window, graphics device and inputs +* rcore_android - Functions to manage window, graphics device and inputs * * PLATFORM: ANDROID * - Android (ARM, ARM64) @@ -48,13 +48,13 @@ #include "rcore.h" -//#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) -#include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others #include // Required for: android_app struct and activity management +#include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others +//#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) #include // Required for: JNIEnv and JavaVM [Used in OpenURL()] #include // Native platform windowing system interface - + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -64,7 +64,7 @@ typedef struct { struct android_poll_source *source; // Android events polling source bool appEnabled; // Flag to detect if app is active ** = true bool contextRebindRequired; // Used to know context rebind required - + // Display data EGLDisplay device; // Native display device (physical screen connection) EGLSurface surface; // Surface to draw on, framebuffers (connected to context) @@ -94,7 +94,7 @@ static GamepadButton AndroidTranslateGamepadButton(int button); // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Application //---------------------------------------------------------------------------------- // To allow easier porting to android, we allow the user to define a @@ -188,6 +188,8 @@ void InitWindow(int width, int height, const char *title) CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; + // Platform specific init window + //-------------------------------------------------------------- // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -269,6 +271,8 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif + // Platform specific close window + //-------------------------------------------------------------- // Close surface, context and display if (platform.device != EGL_NO_DISPLAY) { @@ -289,6 +293,7 @@ void CloseWindow(void) eglTerminate(platform.device); platform.device = EGL_NO_DISPLAY; } + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -586,7 +591,7 @@ double GetTime(void) 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; } diff --git a/src/rcore_custom.c b/src/rcore_custom.c index 2d35a3b4f..799c6054b 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_ - Functions to manage window, graphics device and inputs +* rcore_ - Functions to manage window, graphics device and inputs * * PLATFORM: * - TODO: Define the target platform for the core @@ -48,14 +48,14 @@ #include "rcore.h" -// TODO: Include the platform specific libraries - +// TODO: Include the platform specific libraries + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- typedef struct { // TODO: Define the platform specific variables required - + // Display data EGLDisplay device; // Native display device (physical screen connection) EGLSurface surface; // Surface to draw on, framebuffers (connected to context) @@ -128,15 +128,15 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + CORE.Window.eventWaiting = false; CORE.Window.screen.width = width; CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; // TODO: Initialize window/display system - + // TODO: Initialize input events system // TODO: Initialize assets manager @@ -165,11 +165,10 @@ void CloseWindow(void) rlglClose(); // De-init rlgl -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - - // TODO: Close surface, context and display + // Platform specific close window + //-------------------------------------------------------------- + // TODO. + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -467,7 +466,7 @@ double GetTime(void) 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; } @@ -610,7 +609,7 @@ void PollInputEvents(void) // Reset keys/chars pressed registered CORE.Input.Keyboard.keyPressedQueueCount = 0; CORE.Input.Keyboard.charPressedQueueCount = 0; - + // Reset key repeats for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 2c9872a34..2cf509507 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_desktop - Functions to manage window, graphics device and inputs +* rcore_desktop - Functions to manage window, graphics device and inputs * * PLATFORM: DESKTOP * - Windows (Win32, Win64) @@ -268,12 +268,15 @@ void CloseWindow(void) rlglClose(); // De-init rlgl + // Platform specific close window + //-------------------------------------------------------------- glfwDestroyWindow(platform.handle); glfwTerminate(); #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) timeEndPeriod(1); // Restore time period #endif + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -422,11 +425,11 @@ void ToggleBorderlessWindowed(void) const int monitor = GetCurrentMonitor(); int monitorCount; GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - + if ((monitor >= 0) && (monitor < monitorCount)) { const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - + if (mode) { if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) @@ -1016,7 +1019,7 @@ int GetMonitorHeight(int monitor) else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return height; } @@ -1029,7 +1032,7 @@ int GetMonitorPhysicalWidth(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], &width, NULL); else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return width; } @@ -1042,7 +1045,7 @@ int GetMonitorPhysicalHeight(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &height); else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return height; } @@ -1059,7 +1062,7 @@ int GetMonitorRefreshRate(int monitor) refresh = vidmode->refreshRate; } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return refresh; } @@ -1082,9 +1085,9 @@ Vector2 GetWindowPosition(void) { int x = 0; int y = 0; - + glfwGetWindowPos(platform.handle, &x, &y); - + return (Vector2){ (float)x, (float)y }; } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 50e45c4ae..4a0d805f8 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_drm - Functions to manage window, graphics device and inputs +* rcore_drm - Functions to manage window, graphics device and inputs * * PLATFORM: DRM * - Raspberry Pi 0-5 @@ -81,7 +81,7 @@ typedef struct { pthread_t threadId; // Event reading thread id - + int fd; // File descriptor to the device it is assigned to int eventNum; // Number of 'event' device Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) @@ -111,7 +111,7 @@ typedef struct { // Input data InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" - + // Keyboard data int defaultKeyboardMode; // Default keyboard mode bool eventKeyboardMode; // Keyboard in event mode @@ -307,6 +307,8 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif + // Platform specific close window + //-------------------------------------------------------------- if (platform.prevFB) { drmModeRmFB(platform.fd, platform.prevFB); @@ -392,6 +394,7 @@ void CloseWindow(void) } if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -603,12 +606,12 @@ int GetMonitorPhysicalHeight(int monitor) int GetMonitorRefreshRate(int monitor) { int refresh = 0; - + if ((platform.connector) && (platform.modeIndex >= 0)) { refresh = platform.connector->modes[platform.modeIndex].vrefresh; } - + return refresh; } @@ -718,7 +721,7 @@ double GetTime(void) 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; } @@ -763,7 +766,7 @@ int GetGamepadAxisCount(int gamepad) int axisCount = 0; if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; - + return CORE.Input.Gamepad.axisCount; } @@ -837,10 +840,10 @@ int GetTouchY(void) Vector2 GetTouchPosition(int index) { Vector2 position = { -1.0f, -1.0f }; - + 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); - + return position; } @@ -856,7 +859,7 @@ void PollInputEvents(void) // Reset keys/chars pressed registered CORE.Input.Keyboard.keyPressedQueueCount = 0; CORE.Input.Keyboard.charPressedQueueCount = 0; - + // Reset key repeats for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; @@ -979,14 +982,14 @@ static bool InitGraphicsDevice(int width, int height) } TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors); - + for (size_t i = 0; i < res->count_connectors; i++) { TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i); - + drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]); TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); - + if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) { TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); @@ -1440,7 +1443,7 @@ static void ProcessKeyboard(void) } #endif // SUPPORT_SSH_KEYBOARD_RPI -// Initialise user input from evdev(/dev/input/event) +// Initialise user input from evdev(/dev/input/event) // this means mouse, keyboard or gamepad devices static void InitEvdevInput(void) { diff --git a/src/rcore_web.c b/src/rcore_web.c index 111666045..f4f412f2a 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_web - Functions to manage window, graphics device and inputs +* rcore_web - Functions to manage window, graphics device and inputs * * PLATFORM: WEB * - HTML5 (WebAssembly) @@ -237,7 +237,7 @@ void InitWindow(int width, int height, const char *title) // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - + // Trigger this once to get initial window sizing EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); @@ -282,12 +282,11 @@ void CloseWindow(void) rlglClose(); // De-init rlgl + // Platform specific close window + //-------------------------------------------------------------- glfwDestroyWindow(platform.handle); glfwTerminate(); - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -482,7 +481,7 @@ void SetWindowMinSize(int width, int height) { CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; - + // Trigger the resize event once to update the window minimum width and height if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); } @@ -1090,7 +1089,7 @@ static bool InitGraphicsDevice(int width, int height) } } } - + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, @@ -1229,7 +1228,7 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified) // GLFW3 Window Maximize Callback, runs when window is maximized static void WindowMaximizeCallback(GLFWwindow *window, int maximized) { - + } // GLFW3 WindowFocus Callback, runs when window get/lose focus diff --git a/src/rmodels.c b/src/rmodels.c index 0809fce80..a11ecf799 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5466,11 +5466,11 @@ static Model LoadVOX(const char *fileName) int nbvertices = 0; int meshescount = 0; - + // Read vox file into buffer int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); - + if (fileData == 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX file", fileName); @@ -5575,7 +5575,7 @@ static Model LoadM3D(const char *fileName) m3d_t *m3d = NULL; m3dp_t *prop = NULL; int i, j, k, l, n, mi = -2, vcolor = 0; - + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); @@ -5905,7 +5905,7 @@ static Model LoadM3D(const char *fileName) static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount) { ModelAnimation *animations = NULL; - + m3d_t *m3d = NULL; int i = 0, j = 0; *animCount = 0; From f93d0ff9bc72749e66b99e8813fb41fd7b8bb813 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 11:17:09 +0200 Subject: [PATCH 0015/1037] Update raudio.c --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 61e00a386..c2590e302 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1906,7 +1906,7 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRead = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); + int frameCountRead = (int)drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); frameCountReadTotal += frameCountRead; frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; From cfffa74f96c06231f6fa327e9cc52863b18d52b2 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 11:17:22 +0200 Subject: [PATCH 0016/1037] REVIEWED: Libs include order --- src/rcore.h | 8 ++++---- src/utils.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rcore.h b/src/rcore.h index 7fb35915a..4d40c9567 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -37,12 +37,12 @@ #include "raylib.h" -#include "rlgl.h" +#include "utils.h" // Required for: TRACELOG() macros + +#include "rlgl.h" // Required for: graphics layer functionality #define RAYMATH_IMPLEMENTATION -#include "raymath.h" - -#include "utils.h" // Required for: TRACELOG() macros +#include "raymath.h" // Required for: Vector2/Vector3/Matrix functionality #include // Required for: srand(), rand(), atexit() #include // Required for: sprintf() [Used in OpenURL()] diff --git a/src/utils.h b/src/utils.h index 6ec0f7f1b..ff8246a7b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,7 +32,7 @@ #include // Required for: AAssetManager #endif -#if defined(SUPPORT_TRACELOG) && !defined(TRACELOG) +#if defined(SUPPORT_TRACELOG) #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) #if defined(SUPPORT_TRACELOG_DEBUG) From 540ad9944205235cd9ccbd716c5a667daf929616 Mon Sep 17 00:00:00 2001 From: Purple4pur <49893724+purple4pur@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:05:19 +0800 Subject: [PATCH 0017/1037] Update zig build system to zig version 0.11.0 (#3393) * update build.zig for zig 0.11.0 * fix build.zig in examples to install executable correctly * discard build.zig, only use src/build.zig, to avoid annoying zig-out path problem * update zig version note --- build.zig | 7 ------- examples/build.zig | 36 ++++++++++++++++++++---------------- src/build.zig | 10 +++++----- 3 files changed, 25 insertions(+), 28 deletions(-) delete mode 100644 build.zig diff --git a/build.zig b/build.zig deleted file mode 100644 index 12c0513f6..000000000 --- a/build.zig +++ /dev/null @@ -1,7 +0,0 @@ -const std = @import("std"); -const raylib = @import("src/build.zig"); - -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 -pub fn build(b: *std.Build) void { - raylib.build(b); -} diff --git a/examples/build.zig b/examples/build.zig index 6e13ab3da..383d9f651 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); @@ -11,7 +11,7 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); while (try iter.next()) |entry| { - if (entry.kind != .File) continue; + if (entry.kind != .file) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; const path = try std.fs.path.join(b.allocator, &.{ module, entry.name }); @@ -24,26 +24,26 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT .target = target, .optimize = optimize, }); - exe.addCSourceFile(path, &[_][]const u8{}); + exe.addCSourceFile(.{ .file = .{ .path = path }, .flags = &.{} }); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => "../src/zig-out/lib/raylib.lib", - .linux => "../src/zig-out/lib/libraylib.a", - .macos => "../src/zig-out/lib/libraylib.a", - .emscripten => "../src/zig-out/lib/libraylib.a", + .windows => .{ .path = "../src/zig-out/lib/raylib.lib" }, + .linux => .{ .path = "../src/zig-out/lib/libraylib.a" }, + .macos => .{ .path = "../src/zig-out/lib/libraylib.a" }, + .emscripten => .{ .path = "../src/zig-out/lib/libraylib.a" }, else => @panic("Unsupported OS"), }); - exe.addIncludePath("../src"); - exe.addIncludePath("../src/external"); - exe.addIncludePath("../src/external/glfw/include"); + exe.addIncludePath(.{ .path = "../src" }); + exe.addIncludePath(.{ .path = "../src/external" }); + exe.addIncludePath(.{ .path = "../src/external/glfw/include" }); switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludePath("external/glfw/deps/mingw"); + exe.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); exe.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -71,11 +71,15 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - b.installArtifact(exe); - var run = b.addRunArtifact(exe); - run.cwd = module; - b.step(name, name).dependOn(&run.step); - all.dependOn(&exe.step); + const install_cmd = b.addInstallArtifact(exe, .{}); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(&install_cmd.step); + + const run_step = b.step(name, name); + run_step.dependOn(&run_cmd.step); + + all.dependOn(&install_cmd.step); } return all; } diff --git a/src/build.zig b/src/build.zig index 27250f5ff..3bb4f4213 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", @@ -192,12 +192,12 @@ pub fn build(b: *std.Build) void { const lib = addRaylib(b, target, optimize, options); - lib.installHeader("src/raylib.h", "raylib.h"); - lib.installHeader("src/raymath.h", "raymath.h"); - lib.installHeader("src/rlgl.h", "rlgl.h"); + lib.installHeader("raylib.h", "raylib.h"); + lib.installHeader("raymath.h", "raymath.h"); + lib.installHeader("rlgl.h", "rlgl.h"); if (options.raygui) { - lib.installHeader("../raygui/src/raygui.h", "raygui.h"); + lib.installHeader("../../raygui/src/raygui.h", "raygui.h"); } b.installArtifact(lib); From 0d8a6cfbfa474be123cedff6a4faddfe5443fcec Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 08:48:55 +0200 Subject: [PATCH 0018/1037] Revert "Update zig build system to zig version 0.11.0 (#3393)" This reverts commit 540ad9944205235cd9ccbd716c5a667daf929616. --- build.zig | 7 +++++++ examples/build.zig | 36 ++++++++++++++++-------------------- src/build.zig | 10 +++++----- 3 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 build.zig diff --git a/build.zig b/build.zig new file mode 100644 index 000000000..12c0513f6 --- /dev/null +++ b/build.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const raylib = @import("src/build.zig"); + +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +pub fn build(b: *std.Build) void { + raylib.build(b); +} diff --git a/examples/build.zig b/examples/build.zig index 383d9f651..6e13ab3da 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); @@ -11,7 +11,7 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); while (try iter.next()) |entry| { - if (entry.kind != .file) continue; + if (entry.kind != .File) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; const path = try std.fs.path.join(b.allocator, &.{ module, entry.name }); @@ -24,26 +24,26 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT .target = target, .optimize = optimize, }); - exe.addCSourceFile(.{ .file = .{ .path = path }, .flags = &.{} }); + exe.addCSourceFile(path, &[_][]const u8{}); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => .{ .path = "../src/zig-out/lib/raylib.lib" }, - .linux => .{ .path = "../src/zig-out/lib/libraylib.a" }, - .macos => .{ .path = "../src/zig-out/lib/libraylib.a" }, - .emscripten => .{ .path = "../src/zig-out/lib/libraylib.a" }, + .windows => "../src/zig-out/lib/raylib.lib", + .linux => "../src/zig-out/lib/libraylib.a", + .macos => "../src/zig-out/lib/libraylib.a", + .emscripten => "../src/zig-out/lib/libraylib.a", else => @panic("Unsupported OS"), }); - exe.addIncludePath(.{ .path = "../src" }); - exe.addIncludePath(.{ .path = "../src/external" }); - exe.addIncludePath(.{ .path = "../src/external/glfw/include" }); + exe.addIncludePath("../src"); + exe.addIncludePath("../src/external"); + exe.addIncludePath("../src/external/glfw/include"); switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); + exe.addIncludePath("external/glfw/deps/mingw"); exe.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -71,15 +71,11 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - const install_cmd = b.addInstallArtifact(exe, .{}); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.step.dependOn(&install_cmd.step); - - const run_step = b.step(name, name); - run_step.dependOn(&run_cmd.step); - - all.dependOn(&install_cmd.step); + b.installArtifact(exe); + var run = b.addRunArtifact(exe); + run.cwd = module; + b.step(name, name).dependOn(&run.step); + all.dependOn(&exe.step); } return all; } diff --git a/src/build.zig b/src/build.zig index 3bb4f4213..27250f5ff 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", @@ -192,12 +192,12 @@ pub fn build(b: *std.Build) void { const lib = addRaylib(b, target, optimize, options); - lib.installHeader("raylib.h", "raylib.h"); - lib.installHeader("raymath.h", "raymath.h"); - lib.installHeader("rlgl.h", "rlgl.h"); + lib.installHeader("src/raylib.h", "raylib.h"); + lib.installHeader("src/raymath.h", "raymath.h"); + lib.installHeader("src/rlgl.h", "rlgl.h"); if (options.raygui) { - lib.installHeader("../../raygui/src/raygui.h", "raygui.h"); + lib.installHeader("../raygui/src/raygui.h", "raygui.h"); } b.installArtifact(lib); From f0d949f931e286773aa0d0b87af4f58214ef611b Mon Sep 17 00:00:00 2001 From: Murlocohol Date: Tue, 10 Oct 2023 02:59:09 -0400 Subject: [PATCH 0019/1037] Hotfix for Vector2LineAngle(), should probably be reviewed along with the rest of raylib angle functions to determine what coordinate system we want. (#3394) --- examples/others/raymath_vector_angle.c | 56 +++++++++++++++++--------- src/raymath.h | 5 ++- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index b193648fe..ab8ccf571 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -2,7 +2,7 @@ * * raylib [shapes] example - Vector Angle * -* Example originally created with raylib 1.0, last time updated with raylib 4.2 +* Example originally created with raylib 1.0, last time updated with raylib 4.6 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software @@ -10,7 +10,7 @@ * Copyright (c) 2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ - + #include "raylib.h" #include "raymath.h" @@ -28,7 +28,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [math] example - vector angle"); Vector2 v0 = { screenWidth/2, screenHeight/2 }; - Vector2 v1 = { 100.0f, 80.0f }; + Vector2 v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); Vector2 v2 = { 0 }; // Updated with mouse position float angle = 0.0f; // Angle in degrees @@ -42,21 +42,29 @@ int main(void) { // Update //---------------------------------------------------------------------------------- + float startangle; + + if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; + if (angleMode == 1) startangle = 0.0f; + + v2 = GetMousePosition(); + if (IsKeyPressed(KEY_SPACE)) angleMode = !angleMode; + if(angleMode == 0 && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) v1 = GetMousePosition(); + if (angleMode == 0) { // Calculate angle between two vectors, considering a common origin (v0) - v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); - v2 = GetMousePosition(); - angle = Vector2Angle(Vector2Normalize(Vector2Subtract(v1, v0)), Vector2Normalize(Vector2Subtract(v2, v0)))*RAD2DEG; + Vector2 v1Normal = Vector2Normalize(Vector2Subtract(v1, v0)); + Vector2 v2Normal = Vector2Normalize(Vector2Subtract(v2, v0)); + + angle = Vector2Angle(v1Normal, v2Normal)*RAD2DEG; } else if (angleMode == 1) { // Calculate angle defined by a two vectors line, in reference to horizontal line - v1 = (Vector2){ screenWidth/2, screenHeight/2 }; - v2 = GetMousePosition(); - angle = Vector2LineAngle(v1, v2)*RAD2DEG; + angle = Vector2LineAngle(v0, v2)*RAD2DEG; } //---------------------------------------------------------------------------------- @@ -66,32 +74,40 @@ int main(void) ClearBackground(RAYWHITE); - if (angleMode == 0) DrawText("v0", v0.x, v0.y, 10, DARKGRAY); - DrawText("v1", v1.x, v1.y, 10, DARKGRAY); - DrawText("v2", v2.x, v2.y, 10, DARKGRAY); - if (angleMode == 0) { - DrawText("MODE: Angle between V1 and V2", 10, 10, 20, BLACK); + DrawText("MODE 0: Angle between V1 and V2", 10, 10, 20, BLACK); + DrawText("Right Click to Move V2", 10, 30, 20, DARKGRAY); DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - float startangle = 90 - Vector2LineAngle(v0, v1)*RAD2DEG; - DrawCircleSector(v0, 40.0f, startangle, startangle + angle - 360.0f*(angle > 180.0f), 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { - DrawText("MODE: Angle formed by line V1 to V2", 10, 10, 20, BLACK); + DrawText("MODE 1: Angle formed by line V1 to V2", 10, 10, 20, BLACK); DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); - DrawLineEx(v1, v2, 2.0f, RED); + DrawLineEx(v0, v2, 2.0f, RED); - DrawCircleSector(v1, 40.0f, 90.0f, 180 - angle - 90, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); } + DrawText("v0", v0.x, v0.y, 10, DARKGRAY); + + // If the line from v0 to v1 would overlap the text, move it's position up 10 + if (angleMode == 0 && Vector2Subtract(v0, v1).y > 0.0f) DrawText("v1", v1.x, v1.y-10.0f, 10, DARKGRAY); + if (angleMode == 0 && Vector2Subtract(v0, v1).y < 0.0f) DrawText("v1", v1.x, v1.y, 10, DARKGRAY); + + // If angle mode 1, use v1 to emphasize the horizontal line + if (angleMode == 1) DrawText("v1", v0.x + 40.0f, v0.y, 10, DARKGRAY); + + // position adjusted by -10 so it isn't hidden by cursor + DrawText("v2", v2.x-10.0f, v2.y-10.0f, 10, DARKGRAY); + DrawText("Press SPACE to change MODE", 460, 10, 20, DARKGRAY); - DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 40, 20, LIME); + DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 70, 20, LIME); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/src/raymath.h b/src/raymath.h index db04c51ee..b01b0b22b 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -323,6 +323,8 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) float dot = v1.x*v2.x + v1.y*v2.y; float det = v1.x*v2.y - v1.y*v2.x; + + // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior result = -atan2f(det, dot); return result; @@ -335,7 +337,8 @@ RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) { float result = 0.0f; - result = atan2f(end.y - start.y, end.x - start.x); + // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior + result = -atan2f(end.y - start.y, end.x - start.x); return result; } From 9702a17152d137766d85cd02060219787e3e228b Mon Sep 17 00:00:00 2001 From: Murlocohol Date: Tue, 10 Oct 2023 04:42:11 -0400 Subject: [PATCH 0020/1037] [raymath] Hotfix for Vector2Angle() and Vector2LineAngle() (#3396) * Hotfix for Vector2LineAngle(), should probably be reviewed along with the rest of raylib angle functions to determine what coordinate system we want. * Hotfix for Vector2LineAngle(), should probably be reviewed along with the rest of raylib angle functions to determine what coordinate system we want. * [raymath] Hotfix for Vector2Angle and corresponding example * [raymath] Hotfix for Vector2Angle and corresponding example --------- Co-authored-by: Ray --- examples/others/raymath_vector_angle.c | 10 +++++----- src/raymath.h | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index ab8ccf571..dc6887a41 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -44,7 +44,7 @@ int main(void) //---------------------------------------------------------------------------------- float startangle; - if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; + if (angleMode == 0) startangle = Vector2LineAngle(v0, v1)*RAD2DEG; if (angleMode == 1) startangle = 0.0f; v2 = GetMousePosition(); @@ -81,8 +81,8 @@ int main(void) DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - - DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); + + DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { @@ -90,8 +90,8 @@ int main(void) DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); DrawLineEx(v0, v2, 2.0f, RED); - - DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); + + DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f)); } DrawText("v0", v0.x, v0.y, 10, DARKGRAY); diff --git a/src/raymath.h b/src/raymath.h index b01b0b22b..ff6017039 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -324,8 +324,7 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) float dot = v1.x*v2.x + v1.y*v2.y; float det = v1.x*v2.y - v1.y*v2.x; - // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior - result = -atan2f(det, dot); + result = atan2f(det, dot); return result; } From cb571659565f4555fafd18108e369b706a83775e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 10:48:30 +0200 Subject: [PATCH 0021/1037] REVIEWED: Fix #3387 --- examples/others/raymath_vector_angle.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index dc6887a41..ad573f54d 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -44,7 +44,7 @@ int main(void) //---------------------------------------------------------------------------------- float startangle; - if (angleMode == 0) startangle = Vector2LineAngle(v0, v1)*RAD2DEG; + if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; if (angleMode == 1) startangle = 0.0f; v2 = GetMousePosition(); @@ -91,7 +91,7 @@ int main(void) DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); DrawLineEx(v0, v2, 2.0f, RED); - DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); } DrawText("v0", v0.x, v0.y, 10, DARKGRAY); From 67a1e1ffaeb6d12004028ce50d687ffb4be1cbf7 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 10:48:46 +0200 Subject: [PATCH 0022/1037] Update rtextures.c --- src/rtextures.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index dc7f4af05..8624bbd48 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -664,9 +664,9 @@ void UnloadImage(Image image) // NOTE: File format depends on fileName extension bool ExportImage(Image image, const char *fileName) { - int success = 0; + int result = 0; - if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return success; + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result; #if defined(SUPPORT_IMAGE_EXPORT) int channels = 4; @@ -689,21 +689,21 @@ bool ExportImage(Image image, const char *fileName) { int dataSize = 0; unsigned char *fileData = stbi_write_png_to_mem((const unsigned char *)imgData, image.width*channels, image.width, image.height, channels, &dataSize); - success = SaveFileData(fileName, fileData, dataSize); + result = SaveFileData(fileName, fileData, dataSize); RL_FREE(fileData); } #else if (false) { } #endif #if defined(SUPPORT_FILEFORMAT_BMP) - else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, channels, imgData); + else if (IsFileExtension(fileName, ".bmp")) result = stbi_write_bmp(fileName, image.width, image.height, channels, imgData); #endif #if defined(SUPPORT_FILEFORMAT_TGA) - else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, channels, imgData); + else if (IsFileExtension(fileName, ".tga")) result = stbi_write_tga(fileName, image.width, image.height, channels, imgData); #endif #if defined(SUPPORT_FILEFORMAT_JPG) else if (IsFileExtension(fileName, ".jpg") || - IsFileExtension(fileName, ".jpeg")) success = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100 + IsFileExtension(fileName, ".jpeg")) result = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100 #endif #if defined(SUPPORT_FILEFORMAT_QOI) else if (IsFileExtension(fileName, ".qoi")) @@ -721,30 +721,30 @@ bool ExportImage(Image image, const char *fileName) desc.channels = channels; desc.colorspace = QOI_SRGB; - success = qoi_write(fileName, imgData, &desc); + result = qoi_write(fileName, imgData, &desc); } } #endif #if defined(SUPPORT_FILEFORMAT_KTX) else if (IsFileExtension(fileName, ".ktx")) { - success = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps); + result = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps); } #endif else if (IsFileExtension(fileName, ".raw")) { // Export raw pixel data (without header) // NOTE: It's up to the user to track image parameters - success = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format)); + result = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format)); } if (allocatedData) RL_FREE(imgData); #endif // SUPPORT_IMAGE_EXPORT - if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName); + if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName); - return success; + return result; } // Export image to memory buffer From b94e6290a4e5e96196e23d66a480cdf9d707c380 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 10:50:09 +0200 Subject: [PATCH 0023/1037] Added some comments and tweaks #3313 --- src/rcore_android.c | 3 ++- src/rcore_custom.c | 66 ++++++++++++++++++++++++++++++++++++++++----- src/rcore_desktop.c | 32 +++++++++++----------- src/rcore_drm.c | 26 +++++++++--------- src/rcore_web.c | 21 ++++++++------- 5 files changed, 103 insertions(+), 45 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index a3db7e5fa..6b43b78b4 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -228,7 +228,7 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = platform.app->activity->internalDataPath; - TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); + TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); // Android ALooper_pollAll() variables int pollResult = 0; @@ -247,6 +247,7 @@ void InitWindow(int width, int height, const char *title) //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; } } + //-------------------------------------------------------------- } // Close window and unload OpenGL context diff --git a/src/rcore_custom.c b/src/rcore_custom.c index 799c6054b..933f222fb 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -135,16 +135,70 @@ void InitWindow(int width, int height, const char *title) CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; - // TODO: Initialize window/display system + // Initialize graphics device + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); - // TODO: Initialize input events system + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } + + // Initialize hi-res timer + InitTimer(); - // TODO: Initialize assets manager + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); - // TODO: Initialize base path for storage - //CORE.Storage.basePath = platform.app->activity->internalDataPath; + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + #if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif +#else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes + #endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif - TRACELOG(LOG_INFO, "PLATFORM: Application initialized successfully"); +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + // TODO: Platform specific init window + //-------------------------------------------------------------- + // ... + //-------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); } // Close window and unload OpenGL context diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 2cf509507..4fd86d93e 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -186,23 +186,19 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; - // Initialize graphics device (display device and OpenGL context) + // Initialize graphics device // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Initialize hi-res timer InitTimer(); // Initialize random seed - srand((unsigned int)time(NULL)); + SetRandomSeed((unsigned int)time(NULL)); // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); @@ -248,6 +244,8 @@ void InitWindow(int width, int height, const char *title) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); } // Close window and unload OpenGL context @@ -834,10 +832,12 @@ void SetWindowMinSize(int width, int height) { CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; - int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } @@ -846,10 +846,12 @@ void SetWindowMaxSize(int width, int height) { CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; - int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 4a0d805f8..c7823f820 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -220,19 +220,14 @@ void InitWindow(int width, int height, const char *title) CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } - else - SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); // Initialize hi-res timer InitTimer(); // Initialize random seed - srand((unsigned int)time(NULL)); + SetRandomSeed((unsigned int)time(NULL)); // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); @@ -274,15 +269,20 @@ void InitWindow(int width, int height, const char *title) } #endif - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) - #if defined(SUPPORT_EVENTS_AUTOMATION) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif + + // Platform specific init window + //-------------------------------------------------------------- + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + //-------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } // Close window and unload OpenGL context diff --git a/src/rcore_web.c b/src/rcore_web.c index f4f412f2a..83acae02f 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -176,18 +176,14 @@ void InitWindow(int width, int height, const char *title) CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Initialize hi-res timer InitTimer(); // Initialize random seed - srand((unsigned int)time(NULL)); + SetRandomSeed((unsigned int)time(NULL)); // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); @@ -229,6 +225,13 @@ void InitWindow(int width, int height, const char *title) } #endif +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + // Platform specific init window + //-------------------------------------------------------------- // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); @@ -257,11 +260,9 @@ void InitWindow(int width, int height, const char *title) // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + //-------------------------------------------------------------- -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif + TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); } // Close window and unload OpenGL context From 101a9b04458a38413a29a11f22e6c8e0a472a75b Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 11:59:41 +0200 Subject: [PATCH 0024/1037] Added comments and review some functions #3313 --- src/raylib.h | 10 +- src/rcore.c | 244 ++++++++++++++++++++++++++++---------------- src/rcore_android.c | 9 +- src/rcore_custom.c | 21 ++-- src/rcore_desktop.c | 29 +++--- src/rcore_drm.c | 8 +- src/rcore_web.c | 13 +-- 7 files changed, 191 insertions(+), 143 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 5fa66bd03..5d63d4b62 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1058,15 +1058,16 @@ RLAPI int GetRandomValue(int min, int max); // Get a rando RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) +RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) +// NOTE: Following functions implemented in module [utils] +//------------------------------------------------------------------ RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator RLAPI void MemFree(void *ptr); // Internal memory free -RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) - // Set custom callbacks // WARNING: Callbacks setup is intended for advance users RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log @@ -1083,6 +1084,9 @@ RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success +//------------------------------------------------------------------ + +// File system functions RLAPI bool FileExists(const char *fileName); // Check if file exists RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav) @@ -1120,9 +1124,9 @@ RLAPI bool IsKeyPressedRepeat(int key); // Check if a key RLAPI bool IsKeyDown(int key); // Check if a key is being pressed RLAPI bool IsKeyReleased(int key); // Check if a key has been released once RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed -RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty +RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) // Input-related functions: gamepads RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available diff --git a/src/rcore.c b/src/rcore.c index 29881198a..fa8f50d86 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -315,14 +315,12 @@ const char *TextFormat(const char *text, ...); // Formatting of text with #endif //---------------------------------------------------------------------------------- -// Module Functions Definition - Window and OpenGL Context Functions +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// NOTE: Multiple window/display/monitor management functions have been moved to platform-specific modules - -// Platform-specific functions: -//void InitWindow(int width, int height, const char *title); -//void CloseWindow(void); +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void InitWindow(int width, int height, const char *title) +//void CloseWindow(void) //bool WindowShouldClose(void) //bool IsWindowHidden(void) //bool IsWindowMinimized(void) @@ -364,9 +362,6 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void HideCursor(void) //void EnableCursor(void) //void DisableCursor(void) -//double GetTime(void) -//void TakeScreenshot(const char *fileName) -//void OpenURL(const char *url) // Check if window has been initialized successfully @@ -435,6 +430,19 @@ bool IsCursorOnScreen(void) return CORE.Input.Mouse.cursorOnScreen; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Custom frame control +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void SwapScreenBuffer(void); +//void PollInputEvents(void); +//void WaitTime(double seconds); + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Screen Drawing +//---------------------------------------------------------------------------------- + // Set background color (framebuffer clear color) void ClearBackground(Color color) { @@ -741,6 +749,10 @@ void EndScissorMode(void) rlDisableScissorTest(); } +//---------------------------------------------------------------------------------- +// Module Functions Definition: VR Stereo Rendering +//---------------------------------------------------------------------------------- + // Begin VR drawing configuration void BeginVrStereoMode(VrStereoConfig config) { @@ -837,6 +849,10 @@ void UnloadVrStereoConfig(VrStereoConfig config) TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c"); } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Shaders Management +//---------------------------------------------------------------------------------- + // Load shader from files and bind default locations // NOTE: If shader string is NULL, using default vertex/fragment shaders Shader LoadShader(const char *vsFileName, const char *fsFileName) @@ -1002,6 +1018,10 @@ void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture) } } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Screen-space Queries +//---------------------------------------------------------------------------------- + // Get a ray trace from mouse position Ray GetMouseRay(Vector2 mouse, Camera camera) { @@ -1161,6 +1181,13 @@ Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera) return (Vector2){ transform.x, transform.y }; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Timming +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//double GetTime(void) + // Set target FPS (maximum) void SetTargetFPS(int fps) { @@ -1209,6 +1236,63 @@ float GetFrameTime(void) return (float)CORE.Time.frame; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void OpenURL(const char *url) + +// Get a random value between min and max (both included) +// WARNING: Ranges higher than RAND_MAX will return invalid results +// More specifically, if (max - min) > INT_MAX there will be an overflow, +// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold +int GetRandomValue(int min, int max) +{ + if (min > max) + { + int tmp = max; + max = min; + min = tmp; + } + + if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) + { + TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); + } + + return (rand()%(abs(max - min) + 1) + min); +} + +// Set the seed for the random number generator +void SetRandomSeed(unsigned int seed) +{ + srand(seed); +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[512] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + // Setup window configuration flags (view FLAGS) // NOTE: This function is expected to be called before window creation, // because it sets up some flags for the window creation process. @@ -1221,7 +1305,7 @@ void SetConfigFlags(unsigned int flags) } //---------------------------------------------------------------------------------- -// Module Functions Definition: FileSystem +// Module Functions Definition: File system //---------------------------------------------------------------------------------- // Check if the file exists @@ -1669,36 +1753,9 @@ long GetFileModTime(const char *fileName) } //---------------------------------------------------------------------------------- -// Module Functions Definition: Misc +// Module Functions Definition: Compression and Encoding //---------------------------------------------------------------------------------- -// Get a random value between min and max (both included) -// WARNING: Ranges higher than RAND_MAX will return invalid results -// More specifically, if (max - min) > INT_MAX there will be an overflow, -// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold -int GetRandomValue(int min, int max) -{ - if (min > max) - { - int tmp = max; - max = min; - min = tmp; - } - - if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) - { - TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); - } - - return (rand()%(abs(max - min) + 1) + min); -} - -// Set the seed for the random number generator -void SetRandomSeed(unsigned int seed) -{ - srand(seed); -} - // Compress data (DEFLATE algorithm) unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize) { @@ -1841,26 +1898,9 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) } //---------------------------------------------------------------------------------- -// Module Functions Definition: Inputs +// Module Functions Definition: Input Handling: Keyboard //---------------------------------------------------------------------------------- -// Platform-specific functions -//void SetExitKey(int key) -//const char *GetGamepadName(int gamepad) -//int GetGamepadAxisCount(int gamepad) -//int SetGamepadMappings(const char *mappings) -//int GetMouseX(void) -//int GetMouseY(void) -//Vector2 GetMousePosition(void) -//void SetMousePosition(int x, int y) -//float GetMouseWheelMove(void) -//void SetMouseCursor(int cursor) -//int GetTouchX(void) -//int GetTouchY(void) -//Vector2 GetTouchPosition(int index) -//void SwapScreenBuffer(void) -//void PollInputEvents(void) - // Check if a key has been pressed once bool IsKeyPressed(int key) { @@ -1971,6 +2011,22 @@ int GetCharPressed(void) return value; } +// Set a custom key to exit program +// NOTE: default exitKey is set to ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Input Handling: Gamepad +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//int GetGamepadAxisCount(int gamepad) ** +//const char *GetGamepadName(int gamepad) ** +//int SetGamepadMappings(const char *mappings) + // Check if a gamepad is available bool IsGamepadAvailable(int gamepad) { @@ -1981,16 +2037,11 @@ bool IsGamepadAvailable(int gamepad) return result; } -// Get axis movement vector for a gamepad -float GetGamepadAxisMovement(int gamepad, int axis) -{ - float value = 0; - - if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) && - (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis]; // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA - - return value; -} +// Get gamepad internal name id +//const char *GetGamepadName(int gamepad) +//{ +// return CORE.Input.Gamepad.ready[gamepad]; +//} // Check if a gamepad button has been pressed once bool IsGamepadButtonPressed(int gamepad, int button) @@ -2042,6 +2093,35 @@ int GetGamepadButtonPressed(void) return CORE.Input.Gamepad.lastButtonPressed; } +// Get gamepad axis count +//int GetGamepadAxisCount(int gamepad) +//{ +// return CORE.Input.Gamepad.axisCount; +//} + +// Get axis movement vector for a gamepad +float GetGamepadAxisMovement(int gamepad, int axis) +{ + float value = 0; + + if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) && + (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis]; // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA + + return value; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Input Handling: Mouse +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//int GetMouseX(void) ** +//int GetMouseY(void) ** +//Vector2 GetMousePosition(void) ** +//void SetMousePosition(int x, int y) +//float GetMouseWheelMove(void) ** +//void SetMouseCursor(int cursor) + // Check if a mouse button has been pressed once bool IsMouseButtonPressed(int button) { @@ -2129,6 +2209,15 @@ Vector2 GetMouseWheelMoveV(void) return result; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Input Handling: Touch +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//int GetTouchX(void) +//int GetTouchY(void) +//Vector2 GetTouchPosition(int index) + // Get touch point identifier for given index int GetTouchPointId(int index) { @@ -2329,29 +2418,6 @@ void WaitTime(double seconds) #endif } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[512] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Scan all files and directories in a base path // WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths diff --git a/src/rcore_android.c b/src/rcore_android.c index 6b43b78b4..a9fc5fb9b 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -635,17 +635,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -void SetExitKey(int key) -{ - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); - return NULL; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count diff --git a/src/rcore_custom.c b/src/rcore_custom.c index 933f222fb..e17aa96b1 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -563,17 +563,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -void SetExitKey(int key) -{ - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); - return NULL; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count @@ -592,19 +585,25 @@ int SetGamepadMappings(const char *mappings) // Get mouse position X int GetMouseX(void) { - return (int)CORE.Input.Touch.position[0].x; + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); } // Get mouse position Y int GetMouseY(void) { - return (int)CORE.Input.Touch.position[0].y; + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); } // Get mouse position XY Vector2 GetMousePosition(void) { - return GetTouchPosition(0); + Vector2 position = { 0 }; + + // NOTE: On canvas scaling, mouse position is proportionally returned + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; } // Set mouse position XY diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 4fd86d93e..c410b713b 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -130,6 +130,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area +static void JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -1221,21 +1222,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ - CORE.Input.Keyboard.exitKey = key; -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - const char *name = NULL; - - if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); - - return name; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count @@ -1731,6 +1721,7 @@ static bool InitGraphicsDevice(int width, int height) glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes glfwSetScrollCallback(platform.handle, MouseScrollCallback); glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); + glfwSetJoystickCallback(JoystickCallback); glfwMakeContextCurrent(platform.handle); @@ -2066,5 +2057,17 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) else CORE.Input.Mouse.cursorOnScreen = false; } -// EOF +// GLFW3 Joystick Connected/Disconnected Callback +static void JoystickCallback(int jid, int event) +{ + if (event == GLFW_CONNECTED) + { + strcpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid)); + } + else if (event == GLFW_DISCONNECTED) + { + memset(CORE.Input.Gamepad.name[jid], 0, 64); + } +} +// EOF diff --git a/src/rcore_drm.c b/src/rcore_drm.c index c7823f820..ccf44feb3 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -739,13 +739,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ - CORE.Input.Keyboard.exitKey = key; -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { @@ -764,6 +757,7 @@ const char *GetGamepadName(int gamepad) int GetGamepadAxisCount(int gamepad) { int axisCount = 0; + if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; diff --git a/src/rcore_web.c b/src/rcore_web.c index 83acae02f..ed7dc2657 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -697,21 +697,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ - CORE.Input.Keyboard.exitKey = key; -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - const char *name = NULL; - - name = CORE.Input.Gamepad.name[gamepad]; - - return name; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count From daba1a27945d6be914e71f71fe8e7cfaef1eca5e Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Wed, 11 Oct 2023 04:30:51 -0400 Subject: [PATCH 0025/1037] Split drm update input (#3397) * Update `PLATFORM_DRM` implementation of `GetGamepadAxisCount` * Update * Update `PLATFORM_DRM` implementation of `GetGamepadName` * Add example to test gamepad info functions Fix typo * Update new gamepad info example * Move axis count update out of GamepadThread - race condition * Remove pointless if statement --- examples/Makefile | 1 + examples/core/core_input_gamepad_info.c | 60 +++++++++++++++++++++++++ src/rcore_drm.c | 18 ++------ 3 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 examples/core/core_input_gamepad_info.c diff --git a/examples/Makefile b/examples/Makefile index 9404d39cc..6031f05e9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -365,6 +365,7 @@ CORE = \ core/core_input_mouse \ core/core_input_mouse_wheel \ core/core_input_gamepad \ + core/core_input_gamepad_info \ core/core_input_multitouch \ core/core_input_gestures \ core/core_input_gestures_web \ diff --git a/examples/core/core_input_gamepad_info.c b/examples/core/core_input_gamepad_info.c new file mode 100644 index 000000000..84a687cd8 --- /dev/null +++ b/examples/core/core_input_gamepad_info.c @@ -0,0 +1,60 @@ +/******************************************************************************************* +* +* raylib [core] example - Gamepad information +* +* NOTE: This example requires a Gamepad connected to the system +* Check raylib.h for buttons configuration +* +* Example originally created with raylib 4.6, last time updated with raylib 4.6 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + SetConfigFlags(FLAG_MSAA_4X_HINT); // Set MSAA 4X hint before windows creation + + InitWindow(screenWidth, screenHeight, "raylib [core] example - gamepad information"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + int y = 10; + + BeginDrawing(); + + ClearBackground(RAYWHITE); + + for (int i = 0; i < 4; i++) // by default rcore.h has a MAX_GAMEPADS of 4 so mimmic that here. + { + if (IsGamepadAvailable(i)) + { + DrawText(TextFormat("Gamepad:\n\tName: %s\n\tAxes: %d", GetGamepadName(i), GetGamepadAxisCount(i)), 10, y, 20, BLACK); + y += 40; + } + } + + EndDrawing(); + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- +} \ No newline at end of file diff --git a/src/rcore_drm.c b/src/rcore_drm.c index ccf44feb3..3b688b8a1 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -742,25 +742,12 @@ void OpenURL(const char *url) // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - const char *name = NULL; - - if (CORE.Input.Gamepad.ready[gamepad]) - { - ioctl(platform.gamepadStreamFd[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); - name = CORE.Input.Gamepad.name[gamepad]; - } - - return name; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count int GetGamepadAxisCount(int gamepad) { - int axisCount = 0; - - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); - CORE.Input.Gamepad.axisCount = axisCount; - return CORE.Input.Gamepad.axisCount; } @@ -1959,6 +1946,9 @@ static void InitGamepad(void) if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); } + + ioctl(platform.gamepadStreamFd[i], JSIOCGNAME(64), &CORE.Input.Gamepad.name[i]); + ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount); } } } From ddca5251321ba5542fe59369765b48c1d4c6e5c9 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:11:09 +0200 Subject: [PATCH 0026/1037] RENAMED: `rcore_custom` to `rcore_template` --- src/{rcore_custom.c => rcore_template.c} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{rcore_custom.c => rcore_template.c} (99%) diff --git a/src/rcore_custom.c b/src/rcore_template.c similarity index 99% rename from src/rcore_custom.c rename to src/rcore_template.c index e17aa96b1..3007e4f54 100644 --- a/src/rcore_custom.c +++ b/src/rcore_template.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_ - Functions to manage window, graphics device and inputs +* rcore_ template - Functions to manage window, graphics device and inputs * * PLATFORM: * - TODO: Define the target platform for the core From 6ebfec99c50ef0b00bb693ac25ddebc01457153a Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:14:03 +0200 Subject: [PATCH 0027/1037] Added gamepad functions as generic for all platforms --- src/rcore.c | 18 ++++++++---------- src/rcore_android.c | 12 ------------ src/rcore_desktop.c | 12 ------------ src/rcore_drm.c | 12 ------------ src/rcore_template.c | 12 ------------ src/rcore_web.c | 12 ------------ 6 files changed, 8 insertions(+), 70 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index fa8f50d86..2e6d32132 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2023,8 +2023,6 @@ void SetExitKey(int key) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//int GetGamepadAxisCount(int gamepad) ** -//const char *GetGamepadName(int gamepad) ** //int SetGamepadMappings(const char *mappings) // Check if a gamepad is available @@ -2038,10 +2036,10 @@ bool IsGamepadAvailable(int gamepad) } // Get gamepad internal name id -//const char *GetGamepadName(int gamepad) -//{ -// return CORE.Input.Gamepad.ready[gamepad]; -//} +const char *GetGamepadName(int gamepad) +{ + return CORE.Input.Gamepad.name[gamepad]; +} // Check if a gamepad button has been pressed once bool IsGamepadButtonPressed(int gamepad, int button) @@ -2094,10 +2092,10 @@ int GetGamepadButtonPressed(void) } // Get gamepad axis count -//int GetGamepadAxisCount(int gamepad) -//{ -// return CORE.Input.Gamepad.axisCount; -//} +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} // Get axis movement vector for a gamepad float GetGamepadAxisMovement(int gamepad, int axis) diff --git a/src/rcore_android.c b/src/rcore_android.c index a9fc5fb9b..d9f72894a 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -635,18 +635,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index c410b713b..eaef9517b 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1222,18 +1222,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 3b688b8a1..13d41bbbd 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -739,18 +739,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_template.c b/src/rcore_template.c index 3007e4f54..68a43b2cb 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -563,18 +563,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_web.c b/src/rcore_web.c index ed7dc2657..00691e430 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -697,18 +697,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { From 0d175a69ae1e7072e05587b5f7505bac0c07b4ce Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:36:44 +0200 Subject: [PATCH 0028/1037] REVIEWED: Mouse and Touch functions generic to all platforms #3313 --- src/rcore.c | 65 ++++++++++++++++++++++++++++++++++++------ src/rcore_android.c | 53 +++-------------------------------- src/rcore_desktop.c | 67 +++++--------------------------------------- src/rcore_drm.c | 57 ------------------------------------- src/rcore_template.c | 55 ------------------------------------ src/rcore_web.c | 59 -------------------------------------- 6 files changed, 68 insertions(+), 288 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 2e6d32132..595cbfe02 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2113,11 +2113,7 @@ float GetGamepadAxisMovement(int gamepad, int axis) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//int GetMouseX(void) ** -//int GetMouseY(void) ** -//Vector2 GetMousePosition(void) ** //void SetMousePosition(int x, int y) -//float GetMouseWheelMove(void) ** //void SetMouseCursor(int cursor) // Check if a mouse button has been pressed once @@ -2172,6 +2168,29 @@ bool IsMouseButtonUp(int button) return up; } +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + Vector2 position = { 0 }; + + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; +} + // Get mouse delta between frames Vector2 GetMouseDelta(void) { @@ -2197,6 +2216,17 @@ void SetMouseScale(float scaleX, float scaleY) CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY }; } +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + float result = 0.0f; + + if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; + else result = (float)CORE.Input.Mouse.currentWheelMove.y; + + return result; +} + // Get mouse wheel movement X/Y as a vector Vector2 GetMouseWheelMoveV(void) { @@ -2211,10 +2241,29 @@ Vector2 GetMouseWheelMoveV(void) // Module Functions Definition: Input Handling: Touch //---------------------------------------------------------------------------------- -// NOTE: Functions with a platform-specific implementation on rcore_.c -//int GetTouchX(void) -//int GetTouchY(void) -//Vector2 GetTouchPosition(int index) +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + 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); + + return position; +} // Get touch point identifier for given index int GetTouchPointId(int index) diff --git a/src/rcore_android.c b/src/rcore_android.c index d9f72894a..1921ed79b 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -642,24 +642,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - return GetTouchPosition(0); -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -667,43 +649,12 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); - return 0.0f; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - 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); - - return position; -} - // Register all input events void PollInputEvents(void) { @@ -1247,6 +1198,10 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; + + // Map touch[0] as mouse input for convenience + CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0]; + CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; return 0; } diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index eaef9517b..8c4b73c8c 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1228,29 +1228,6 @@ int SetGamepadMappings(const char *mappings) return glfwUpdateGamepadMappings(mappings); } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; - - return position; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -1261,17 +1238,6 @@ void SetMousePosition(int x, int y) glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - float result = 0.0f; - - if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; - else result = (float)CORE.Input.Mouse.currentWheelMove.y; - - return result; -} - // Set mouse cursor void SetMouseCursor(int cursor) { @@ -1284,32 +1250,6 @@ void SetMouseCursor(int cursor) } } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return GetMouseX(); -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return GetMouseY(); -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - // TODO: GLFW does not support multi-touch input just yet - // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch - // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages - if (index == 0) position = GetMousePosition(); - - return position; -} - // Register all input events void PollInputEvents(void) { @@ -1352,6 +1292,13 @@ void PollInputEvents(void) // Reset touch positions //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Map touch position to mouse position for convenience + // WARNING: If the target desktop device supports touch screen, this behavious should be reviewed! + // TODO: GLFW does not support multi-touch input just yet + // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch + // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; // Check if gamepads are ready // NOTE: We do it here in case of disconnection diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 13d41bbbd..839edeb47 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -746,29 +746,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; - - return position; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -776,46 +753,12 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - float result = 0.0f; - - if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; - else result = (float)CORE.Input.Mouse.currentWheelMove.y; - - return result; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return GetMouseX(); -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return GetMouseY(); -} - -// Get touch position XY for a touch point index (relative to screen size) -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - 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); - - return position; -} - // Register all input events void PollInputEvents(void) { diff --git a/src/rcore_template.c b/src/rcore_template.c index 68a43b2cb..b6f0ff97e 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -570,30 +570,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - // NOTE: On canvas scaling, mouse position is proportionally returned - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; - - return position; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -601,43 +577,12 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); - return 0.0f; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - 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); - - return position; -} - // Register all input events void PollInputEvents(void) { diff --git a/src/rcore_web.c b/src/rcore_web.c index 00691e430..d6af5a6de 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -705,30 +705,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - // NOTE: On canvas scaling, mouse position is proportionally returned - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; - - return position; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -739,47 +715,12 @@ void SetMousePosition(int x, int y) glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - float result = 0.0f; - - if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; - else result = (float)CORE.Input.Mouse.currentWheelMove.y; - - return result; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_INFO, "SetMouseCursor not implemented in rcore_web.c"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - 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); - - return position; -} - // Register all input events void PollInputEvents(void) { From a2c5f01059e9c8efd951c06a87a37e39120daf00 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:55:12 +0200 Subject: [PATCH 0029/1037] Reordered one function --- src/rcore.c | 2 +- src/rcore_android.c | 12 ++++----- src/rcore_desktop.c | 58 ++++++++++++++++++++++---------------------- src/rcore_drm.c | 12 ++++----- src/rcore_template.c | 12 ++++----- src/rcore_web.c | 12 ++++----- 6 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 595cbfe02..8b5b3bf49 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2285,7 +2285,7 @@ int GetTouchPointCount(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Platform-specific functions +// NOTE: Functions with a platform-specific implementation on rcore_.c //static bool InitGraphicsDevice(int width, int height) // Set viewport for a provided width and height diff --git a/src/rcore_android.c b/src/rcore_android.c index 1921ed79b..4413dfa87 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -347,6 +347,12 @@ 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) { @@ -365,12 +371,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 8c4b73c8c..54b684a0c 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -380,35 +380,6 @@ void ToggleFullscreen(void) if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1); } -// Set window state: maximized, if resizable -void MaximizeWindow(void) -{ - if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - glfwMaximizeWindow(platform.handle); - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; - } -} - -// Set window state: minimized -void MinimizeWindow(void) -{ - // NOTE: Following function launches callback that sets appropriate flag! - glfwIconifyWindow(platform.handle); -} - -// Set window state: not minimized/maximized -void RestoreWindow(void) -{ - if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - // Restores the specified window if it was previously iconified (minimized) or maximized - glfwRestoreWindow(platform.handle); - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; - CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; - } -} - // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { @@ -484,6 +455,35 @@ void ToggleBorderlessWindowed(void) else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); } +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + glfwMaximizeWindow(platform.handle); + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; + } +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + // NOTE: Following function launches callback that sets appropriate flag! + glfwIconifyWindow(platform.handle); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + // Restores the specified window if it was previously iconified (minimized) or maximized + glfwRestoreWindow(platform.handle); + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + } +} + // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 839edeb47..459444ada 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -448,6 +448,12 @@ 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) { @@ -466,12 +472,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_template.c b/src/rcore_template.c index b6f0ff97e..5b6507eb8 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -275,6 +275,12 @@ 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) { @@ -293,12 +299,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_web.c b/src/rcore_web.c index d6af5a6de..d24ef1214 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -410,6 +410,12 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag } +// 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) { @@ -428,12 +434,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { From da9c2894feee6d64780d9cfe161255c4db91decc Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 12:10:38 +0200 Subject: [PATCH 0030/1037] Reorganized some functions, `WaitTime()` is common to all platforms --- src/raylib.h | 18 +++--- src/rcore.c | 157 +++++++++++++++++++++++++-------------------------- 2 files changed, 87 insertions(+), 88 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 5d63d4b62..c03e0a576 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -986,14 +986,6 @@ RLAPI const char *GetClipboardText(void); // Get clipboa RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling -// Custom frame control functions -// NOTE: Those functions are intended for advance users that want full control over the frame processing -// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() -// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL -RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) -RLAPI void PollInputEvents(void); // Register all input events -RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) - // Cursor-related functions RLAPI void ShowCursor(void); // Shows cursor RLAPI void HideCursor(void); // Hides cursor @@ -1049,9 +1041,17 @@ RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the // Timing-related functions RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) -RLAPI int GetFPS(void); // Get current FPS RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() +RLAPI int GetFPS(void); // Get current FPS + +// Custom frame control functions +// NOTE: Those functions are intended for advance users that want full control over the frame processing +// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() +// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL +RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) +RLAPI void PollInputEvents(void); // Register all input events +RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) // Misc. functions RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) diff --git a/src/rcore.c b/src/rcore.c index 8b5b3bf49..18d2e2929 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -430,15 +430,6 @@ bool IsCursorOnScreen(void) return CORE.Input.Mouse.cursorOnScreen; } -//---------------------------------------------------------------------------------- -// Module Functions Definition: Custom frame control -//---------------------------------------------------------------------------------- - -// NOTE: Functions with a platform-specific implementation on rcore_.c -//void SwapScreenBuffer(void); -//void PollInputEvents(void); -//void WaitTime(double seconds); - //---------------------------------------------------------------------------------- // Module Functions Definition: Screen Drawing //---------------------------------------------------------------------------------- @@ -1236,6 +1227,60 @@ float GetFrameTime(void) return (float)CORE.Time.frame; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Custom frame control +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void SwapScreenBuffer(void); +//void PollInputEvents(void); + +// Wait for some time (stop program execution) +// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could +// take longer than expected... for that reason we use the busy wait loop +// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected +// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32! +void WaitTime(double seconds) +{ + if (seconds < 0) return; + +#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) + double destinationTime = GetTime() + seconds; +#endif + +#if defined(SUPPORT_BUSY_WAIT_LOOP) + while (GetTime() < destinationTime) { } +#else + #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) + double sleepSeconds = seconds - seconds*0.05; // NOTE: We reserve a percentage of the time for busy waiting + #else + double sleepSeconds = seconds; + #endif + + // System halt functions + #if defined(_WIN32) + Sleep((unsigned long)(sleepSeconds*1000.0)); + #endif + #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) + struct timespec req = { 0 }; + time_t sec = sleepSeconds; + long nsec = (sleepSeconds - sec)*1000000000L; + req.tv_sec = sec; + req.tv_nsec = nsec; + + // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated. + while (nanosleep(&req, &req) == -1) continue; + #endif + #if defined(__APPLE__) + usleep(sleepSeconds*1000000.0); + #endif + + #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) + while (GetTime() < destinationTime) { } + #endif +#endif +} + //---------------------------------------------------------------------------------- // Module Functions Definition: Misc //---------------------------------------------------------------------------------- @@ -2288,6 +2333,30 @@ int GetTouchPointCount(void) // NOTE: Functions with a platform-specific implementation on rcore_.c //static bool InitGraphicsDevice(int width, int height) +// Initialize hi-resolution timer +void InitTimer(void) +{ +// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. +// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. +// High resolutions can also prevent the CPU power management system from entering power-saving modes. +// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) +#endif + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) + struct timespec now = { 0 }; + + if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success + { + CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; + } + else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available"); +#endif + + CORE.Time.previous = GetTime(); // Get time as double +} + // Set viewport for a provided width and height void SetupViewport(int width, int height) { @@ -2395,76 +2464,6 @@ void SetupFramebuffer(int width, int height) } } -// Initialize hi-resolution timer -void InitTimer(void) -{ -// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. -// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. -// High resolutions can also prevent the CPU power management system from entering power-saving modes. -// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) -#endif - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) - struct timespec now = { 0 }; - - if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success - { - CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; - } - else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available"); -#endif - - CORE.Time.previous = GetTime(); // Get time as double -} - -// Wait for some time (stop program execution) -// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could -// take longer than expected... for that reason we use the busy wait loop -// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected -// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32! -void WaitTime(double seconds) -{ - if (seconds < 0) return; - -#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) - double destinationTime = GetTime() + seconds; -#endif - -#if defined(SUPPORT_BUSY_WAIT_LOOP) - while (GetTime() < destinationTime) { } -#else - #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) - double sleepSeconds = seconds - seconds*0.05; // NOTE: We reserve a percentage of the time for busy waiting - #else - double sleepSeconds = seconds; - #endif - - // System halt functions - #if defined(_WIN32) - Sleep((unsigned long)(sleepSeconds*1000.0)); - #endif - #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) - struct timespec req = { 0 }; - time_t sec = sleepSeconds; - long nsec = (sleepSeconds - sec)*1000000000L; - req.tv_sec = sec; - req.tv_nsec = nsec; - - // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated. - while (nanosleep(&req, &req) == -1) continue; - #endif - #if defined(__APPLE__) - usleep(sleepSeconds*1000000.0); - #endif - - #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) - while (GetTime() < destinationTime) { } - #endif -#endif -} - // Scan all files and directories in a base path // WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths From 28fb58f0ea11f0b30b8aaf79d7bb148491ed8775 Mon Sep 17 00:00:00 2001 From: Murlocohol Date: Wed, 11 Oct 2023 06:15:40 -0400 Subject: [PATCH 0031/1037] [rtext] TextFormat() warn user if buffer overflow occured. (#3399) * [rtext] TextFormat now alerts user to truncation. * Update rtext.c * Update rcore.c * Update rtext.c --- src/rcore.c | 12 +++++++++++- src/rtext.c | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 18d2e2929..c1c9980b2 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2922,6 +2922,7 @@ static void PlayAutomationEvent(unsigned int frame) #if !defined(SUPPORT_MODULE_RTEXT) // Formatting of text with variables to 'embed' // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times + const char *TextFormat(const char *text, ...) { #ifndef MAX_TEXTFORMAT_BUFFERS @@ -2940,12 +2941,21 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); + // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + { + // We are going to insert [TRUN] at the end of the string so the user knows what happened + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + sprintf(truncBuffer, "[TRUN]"); + } + index += 1; // Move to next buffer for next function call if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; return currentBuffer; } + #endif // !SUPPORT_MODULE_RTEXT diff --git a/src/rtext.c b/src/rtext.c index fb8440131..16b65507b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1371,15 +1371,24 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); + // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + { + // We are going to insert [TRUN] at the end of the string so the user knows what happened + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + sprintf(truncBuffer, "[TRUN]"); + } + index += 1; // Move to next buffer for next function call if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; return currentBuffer; } + // Get integer value from text // NOTE: This function replaces atoi() [stdlib.h] int TextToInteger(const char *text) From 61af8e76310cfc23015e0eaee99c55fb72714a30 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 12:20:03 +0200 Subject: [PATCH 0032/1037] REVIEWED: #3399, Fix #3366 --- src/rcore.c | 9 ++++----- src/rtext.c | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index c1c9980b2..e7868ebb8 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2922,7 +2922,6 @@ static void PlayAutomationEvent(unsigned int frame) #if !defined(SUPPORT_MODULE_RTEXT) // Formatting of text with variables to 'embed' // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times - const char *TextFormat(const char *text, ...) { #ifndef MAX_TEXTFORMAT_BUFFERS @@ -2941,14 +2940,14 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); - // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured - if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six char + '\0' sprintf(truncBuffer, "[TRUN]"); } diff --git a/src/rtext.c b/src/rtext.c index 16b65507b..593fdb53f 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1371,14 +1371,14 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); - // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured - if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six chars + '\0' sprintf(truncBuffer, "[TRUN]"); } From 6ed8acde6730c34a7e127b16ab567a5f3438b29b Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:29:21 -0300 Subject: [PATCH 0033/1037] Fix windowMin/Max to screenMin/Max for android, drm, template (#3400) --- src/rcore.h | 2 -- src/rcore_android.c | 10 +++++----- src/rcore_drm.c | 18 +++++++++--------- src/rcore_template.c | 12 ++++++------ 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/rcore.h b/src/rcore.h index 4d40c9567..dbff6ab13 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -127,8 +127,6 @@ typedef struct CoreData { Point renderOffset; // Offset from render area (must be divided by 2) Size screenMin; // Screen minimum width and height (for resizable window) Size screenMax; // Screen maximum width and height (for resizable window) - Size windowMin; // Window minimum width and height - Size windowMax; // Window maximum width and height Matrix screenScale; // Matrix to scale screen (framebuffer rendering) char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) diff --git a/src/rcore_android.c b/src/rcore_android.c index 4413dfa87..bfb57fedf 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -416,15 +416,15 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = 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.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; } // Set window dimensions @@ -1198,7 +1198,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; - + // Map touch[0] as mouse input for convenience CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0]; CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 459444ada..2f0eab682 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -281,7 +281,7 @@ void InitWindow(int width, int height, const char *title) InitGamepad(); // Gamepad init InitKeyboard(); // Keyboard init (stdin) //-------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -517,15 +517,15 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = 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.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; } // Set window dimensions @@ -841,10 +841,10 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default // Set the window minimum and maximum default values to 0 - CORE.Window.windowMin.width = 0; - CORE.Window.windowMin.height = 0; - CORE.Window.windowMax.width = 0; - CORE.Window.windowMax.height = 0; + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) diff --git a/src/rcore_template.c b/src/rcore_template.c index 5b6507eb8..ea5af40d9 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -141,7 +141,7 @@ void InitWindow(int width, int height, const char *title) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - + // Initialize hi-res timer InitTimer(); @@ -150,7 +150,7 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -344,15 +344,15 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = 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.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; } // Set window dimensions From 876e6b3a0d4db8e6124bf8f95d048879f2f760b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 20:25:09 +0200 Subject: [PATCH 0034/1037] REVIEWED: `TextFormat()`, added "..." for truncation #3366 It seems more standard than [TRUN] --- src/rcore.c | 6 +++--- src/rtext.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index e7868ebb8..253436e51 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2946,9 +2946,9 @@ const char *TextFormat(const char *text, ...) // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { - // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six char + '\0' - sprintf(truncBuffer, "[TRUN]"); + // Inserting "..." at the end of the string to mark as truncated + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0" + sprintf(truncBuffer, "..."); } index += 1; // Move to next buffer for next function call diff --git a/src/rtext.c b/src/rtext.c index 593fdb53f..2d72bbe6b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1377,9 +1377,9 @@ const char *TextFormat(const char *text, ...) // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { - // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six chars + '\0' - sprintf(truncBuffer, "[TRUN]"); + // Inserting "..." at the end of the string to mark as truncated + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0" + sprintf(truncBuffer, "..."); } index += 1; // Move to next buffer for next function call From 2e65bc675ce2ef92ccc784e25739b27edd7be94b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 13 Oct 2023 14:14:16 +0200 Subject: [PATCH 0035/1037] Moved some platforms functions to generic `rcore` #3313 Reviewed `InitWindow()` to clearly note platform specific code --- src/rcore.c | 53 +++++++++++++++++----- src/rcore_android.c | 45 +++++-------------- src/rcore_desktop.c | 70 ++++++++++------------------- src/rcore_drm.c | 60 ++++++++----------------- src/rcore_template.c | 48 ++++---------------- src/rcore_web.c | 102 ++++++++++++++++--------------------------- 6 files changed, 142 insertions(+), 236 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 253436e51..3efa67b2c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -322,18 +322,15 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void InitWindow(int width, int height, const char *title) //void CloseWindow(void) //bool WindowShouldClose(void) -//bool IsWindowHidden(void) -//bool IsWindowMinimized(void) -//bool IsWindowMaximized(void) -//bool IsWindowFocused(void) -//bool IsWindowResized(void) //void ToggleFullscreen(void) +//void ToggleBorderlessWindowed(void) //void MaximizeWindow(void) //void MinimizeWindow(void) //void RestoreWindow(void) -//void ToggleBorderlessWindowed(void) + //void SetWindowState(unsigned int flags) //void ClearWindowState(unsigned int flags) + //void SetWindowIcon(Image image) //void SetWindowIcons(Image *images, int count) //void SetWindowTitle(const char *title) @@ -345,25 +342,27 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void SetWindowOpacity(float opacity) //void SetWindowFocused(void) //void *GetWindowHandle(void) +//Vector2 GetWindowPosition(void) +//Vector2 GetWindowScaleDPI(void) + //int GetMonitorCount(void) //int GetCurrentMonitor(void) -//Vector2 GetMonitorPosition(int monitor) //int GetMonitorWidth(int monitor) //int GetMonitorHeight(int monitor) //int GetMonitorPhysicalWidth(int monitor) //int GetMonitorPhysicalHeight(int monitor) //int GetMonitorRefreshRate(int monitor) +//Vector2 GetMonitorPosition(int monitor) //const char *GetMonitorName(int monitor) -//Vector2 GetWindowPosition(void) -//Vector2 GetWindowScaleDPI(void) + //void SetClipboardText(const char *text) //const char *GetClipboardText(void) + //void ShowCursor(void) //void HideCursor(void) //void EnableCursor(void) //void DisableCursor(void) - // Check if window has been initialized successfully bool IsWindowReady(void) { @@ -376,6 +375,36 @@ bool IsWindowFullscreen(void) return CORE.Window.fullscreen; } +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return CORE.Window.resizedLastFrame; +} + // Check if one specific window flag is enabled bool IsWindowState(unsigned int flag) { @@ -394,13 +423,13 @@ int GetScreenHeight(void) return CORE.Window.screen.height; } -// Get current render width which is equal to screen width * dpi scale +// Get current render width which is equal to screen width*dpi scale int GetRenderWidth(void) { return CORE.Window.render.width; } -// Get current screen height which is equal to screen height * dpi scale +// Get current screen height which is equal to screen height*dpi scale int GetRenderHeight(void) { return CORE.Window.render.height; diff --git a/src/rcore_android.c b/src/rcore_android.c index bfb57fedf..7cc71bbca 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -183,13 +183,14 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- CORE.Window.screen.width = width; CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; - - // Platform specific init window - //-------------------------------------------------------------- + // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -227,6 +228,12 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = platform.app->activity->internalDataPath; + + // Set some default window flags + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED); // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED); // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED); // false TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); @@ -311,36 +318,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return platform.appEnabled; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return false; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { @@ -936,12 +913,14 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_GAINED_FOCUS: { platform.appEnabled = true; + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; //ResumeMusicStream(); } break; case APP_CMD_PAUSE: break; case APP_CMD_LOST_FOCUS: { platform.appEnabled = false; + CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; //PauseMusicStream(); } break; case APP_CMD_TERM_WINDOW: diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 54b684a0c..c2e5b23f1 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -2,7 +2,7 @@ * * rcore_desktop - Functions to manage window, graphics device and inputs * -* PLATFORM: DESKTOP +* PLATFORM: DESKTOP: GLFW * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) * - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) @@ -187,8 +187,26 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; + + glfwInitAllocator(&allocator); +*/ + // Initialize graphics device // NOTE: returns true if window and graphic device has been initialized successfully + // WARNING: Actually, all window initialization and input callbacks initialization is + // done inside InitGraphicsDevice(), this functionality should be changed! CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program @@ -197,13 +215,15 @@ void InitWindow(int width, int height, const char *title) // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -304,36 +324,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return CORE.Window.resizedLastFrame; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { @@ -1408,18 +1398,6 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - - glfwInitAllocator(&allocator); -*/ #if defined(__APPLE__) glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); #endif diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 2f0eab682..c0e88c723 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -215,6 +215,9 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- // Initialize graphics device (display device and OpenGL context) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); @@ -223,15 +226,28 @@ void InitWindow(int width, int height, const char *title) if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); + // Set some default window flags + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED); // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED); // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED); // false + // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -274,14 +290,6 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif - // Platform specific init window - //-------------------------------------------------------------- - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) - //-------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -412,36 +420,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return true; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return false; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { diff --git a/src/rcore_template.c b/src/rcore_template.c index ea5af40d9..88b3c4a7e 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -128,8 +128,11 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + + + // TODO: Platform specific init window + //-------------------------------------------------------------- CORE.Window.screen.width = width; CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; @@ -144,13 +147,15 @@ void InitWindow(int width, int height, const char *title) // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -193,11 +198,6 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif - // TODO: Platform specific init window - //-------------------------------------------------------------- - // ... - //-------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); } @@ -239,36 +239,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return platform.appEnabled; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return false; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { diff --git a/src/rcore_web.c b/src/rcore_web.c index d24ef1214..261498a5b 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -171,6 +171,9 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- // Initialize graphics device (display device and OpenGL context) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); @@ -181,13 +184,44 @@ void InitWindow(int width, int height, const char *title) // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Setup callback functions for the DOM events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); + + // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review + // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) + // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + + // Trigger this once to get initial window sizing + EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); + + // Support keyboard events -> Not used, GLFW.JS takes care of that + // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + + // Support mouse events + emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); + + // Support touch events + emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + + // Support gamepad events (not provided by GLFW3 on emscripten) + emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -230,38 +264,6 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif - // Platform specific init window - //-------------------------------------------------------------- - // Setup callback functions for the DOM events - emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - - // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review - // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) - // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - - // Trigger this once to get initial window sizing - EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - - // Support keyboard events -> Not used, GLFW.JS takes care of that - // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - - // Support mouse events - emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); - - // Support touch events - emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - - // Support gamepad events (not provided by GLFW3 on emscripten) - emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); - emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); - //-------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); } @@ -309,36 +311,6 @@ bool WindowShouldClose(void) return false; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return CORE.Window.resizedLastFrame; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { From 0daa5ce1e71963577a2bcf6140df2d71d8e6c730 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:36:42 +0200 Subject: [PATCH 0036/1037] Fix `GetMouseDelta()` issue for Android (#3404) --- src/rcore_android.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/rcore_android.c b/src/rcore_android.c index 7cc71bbca..5f6f34bab 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -1178,6 +1178,16 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; + // Stores the previous position of touch[0] only while it's active to calculate the delta. + if (flags == AMOTION_EVENT_ACTION_MOVE) + { + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + } + else + { + CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0]; + } + // Map touch[0] as mouse input for convenience CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0]; CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; From 0f4a8cf7cb18dbd3594c77c5b2558f6cc979f2ef Mon Sep 17 00:00:00 2001 From: Babak Date: Fri, 13 Oct 2023 16:37:35 +0200 Subject: [PATCH 0037/1037] Ported to stb_image_resize2.h (#3403) --- src/external/stb_image_resize.h | 2634 -------- src/external/stb_image_resize2.h | 10303 +++++++++++++++++++++++++++++ src/rtextures.c | 12 +- 3 files changed, 10309 insertions(+), 2640 deletions(-) delete mode 100644 src/external/stb_image_resize.h create mode 100644 src/external/stb_image_resize2.h diff --git a/src/external/stb_image_resize.h b/src/external/stb_image_resize.h deleted file mode 100644 index ef9e6fe87..000000000 --- a/src/external/stb_image_resize.h +++ /dev/null @@ -1,2634 +0,0 @@ -/* stb_image_resize - v0.97 - public domain image resizing - by Jorge L Rodriguez (@VinoBS) - 2014 - http://github.com/nothings/stb - - Written with emphasis on usability, portability, and efficiency. (No - SIMD or threads, so it be easily outperformed by libs that use those.) - Only scaling and translation is supported, no rotations or shears. - Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. - - COMPILING & LINKING - In one C/C++ file that #includes this file, do this: - #define STB_IMAGE_RESIZE_IMPLEMENTATION - before the #include. That will create the implementation in that file. - - QUICKSTART - stbir_resize_uint8( input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, num_channels) - stbir_resize_float(...) - stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, - num_channels , alpha_chan , 0) - stbir_resize_uint8_srgb_edgemode( - input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, - num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) - // WRAP/REFLECT/ZERO - - FULL API - See the "header file" section of the source for API documentation. - - ADDITIONAL DOCUMENTATION - - SRGB & FLOATING POINT REPRESENTATION - The sRGB functions presume IEEE floating point. If you do not have - IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use - a slower implementation. - - MEMORY ALLOCATION - The resize functions here perform a single memory allocation using - malloc. To control the memory allocation, before the #include that - triggers the implementation, do: - - #define STBIR_MALLOC(size,context) ... - #define STBIR_FREE(ptr,context) ... - - Each resize function makes exactly one call to malloc/free, so to use - temp memory, store the temp memory in the context and return that. - - ASSERT - Define STBIR_ASSERT(boolval) to override assert() and not use assert.h - - OPTIMIZATION - Define STBIR_SATURATE_INT to compute clamp values in-range using - integer operations instead of float operations. This may be faster - on some platforms. - - DEFAULT FILTERS - For functions which don't provide explicit control over what filters - to use, you can change the compile-time defaults with - - #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something - #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something - - See stbir_filter in the header-file section for the list of filters. - - NEW FILTERS - A number of 1D filter kernels are used. For a list of - supported filters see the stbir_filter enum. To add a new filter, - write a filter function and add it to stbir__filter_info_table. - - PROGRESS - For interactive use with slow resize operations, you can install - a progress-report callback: - - #define STBIR_PROGRESS_REPORT(val) some_func(val) - - The parameter val is a float which goes from 0 to 1 as progress is made. - - For example: - - static void my_progress_report(float progress); - #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) - - #define STB_IMAGE_RESIZE_IMPLEMENTATION - #include "stb_image_resize.h" - - static void my_progress_report(float progress) - { - printf("Progress: %f%%\n", progress*100); - } - - MAX CHANNELS - If your image has more than 64 channels, define STBIR_MAX_CHANNELS - to the max you'll have. - - ALPHA CHANNEL - Most of the resizing functions provide the ability to control how - the alpha channel of an image is processed. The important things - to know about this: - - 1. The best mathematically-behaved version of alpha to use is - called "premultiplied alpha", in which the other color channels - have had the alpha value multiplied in. If you use premultiplied - alpha, linear filtering (such as image resampling done by this - library, or performed in texture units on GPUs) does the "right - thing". While premultiplied alpha is standard in the movie CGI - industry, it is still uncommon in the videogame/real-time world. - - If you linearly filter non-premultiplied alpha, strange effects - occur. (For example, the 50/50 average of 99% transparent bright green - and 1% transparent black produces 50% transparent dark green when - non-premultiplied, whereas premultiplied it produces 50% - transparent near-black. The former introduces green energy - that doesn't exist in the source image.) - - 2. Artists should not edit premultiplied-alpha images; artists - want non-premultiplied alpha images. Thus, art tools generally output - non-premultiplied alpha images. - - 3. You will get best results in most cases by converting images - to premultiplied alpha before processing them mathematically. - - 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the - resizer does not do anything special for the alpha channel; - it is resampled identically to other channels. This produces - the correct results for premultiplied-alpha images, but produces - less-than-ideal results for non-premultiplied-alpha images. - - 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, - then the resizer weights the contribution of input pixels - based on their alpha values, or, equivalently, it multiplies - the alpha value into the color channels, resamples, then divides - by the resultant alpha value. Input pixels which have alpha=0 do - not contribute at all to output pixels unless _all_ of the input - pixels affecting that output pixel have alpha=0, in which case - the result for that pixel is the same as it would be without - STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for - input images in integer formats. For input images in float format, - input pixels with alpha=0 have no effect, and output pixels - which have alpha=0 will be 0 in all channels. (For float images, - you can manually achieve the same result by adding a tiny epsilon - value to the alpha channel of every image, and then subtracting - or clamping it at the end.) - - 6. You can suppress the behavior described in #5 and make - all-0-alpha pixels have 0 in all channels by #defining - STBIR_NO_ALPHA_EPSILON. - - 7. You can separately control whether the alpha channel is - interpreted as linear or affected by the colorspace. By default - it is linear; you almost never want to apply the colorspace. - (For example, graphics hardware does not apply sRGB conversion - to the alpha channel.) - - CONTRIBUTORS - Jorge L Rodriguez: Implementation - Sean Barrett: API design, optimizations - Aras Pranckevicius: bugfix - Nathan Reed: warning fixes - - REVISIONS - 0.97 (2020-02-02) fixed warning - 0.96 (2019-03-04) fixed warnings - 0.95 (2017-07-23) fixed warnings - 0.94 (2017-03-18) fixed warnings - 0.93 (2017-03-03) fixed bug with certain combinations of heights - 0.92 (2017-01-02) fix integer overflow on large (>2GB) images - 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions - 0.90 (2014-09-17) first released version - - LICENSE - See end of file for license information. - - TODO - Don't decode all of the image data when only processing a partial tile - Don't use full-width decode buffers when only processing a partial tile - When processing wide images, break processing into tiles so data fits in L1 cache - Installable filters? - Resize that respects alpha test coverage - (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: - https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) -*/ - -#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H -#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H - -#ifdef _MSC_VER -typedef unsigned char stbir_uint8; -typedef unsigned short stbir_uint16; -typedef unsigned int stbir_uint32; -#else -#include -typedef uint8_t stbir_uint8; -typedef uint16_t stbir_uint16; -typedef uint32_t stbir_uint32; -#endif - -#ifndef STBIRDEF -#ifdef STB_IMAGE_RESIZE_STATIC -#define STBIRDEF static -#else -#ifdef __cplusplus -#define STBIRDEF extern "C" -#else -#define STBIRDEF extern -#endif -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Easy-to-use API: -// -// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) -// * input_w is input image width (x-axis), input_h is input image height (y-axis) -// * stride is the offset between successive rows of image data in memory, in bytes. you can -// specify 0 to mean packed continuously in memory -// * alpha channel is treated identically to other channels. -// * colorspace is linear or sRGB as specified by function name -// * returned result is 1 for success or 0 in case of an error. -// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. -// * Memory required grows approximately linearly with input and output size, but with -// discontinuities at input_w == output_w and input_h == output_h. -// * These functions use a "default" resampling filter defined at compile time. To change the filter, -// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE -// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. - -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - - -// The following functions interpret image data as gamma-corrected sRGB. -// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, -// or otherwise provide the index of the alpha channel. Flags value -// of 0 will probably do the right thing if you're not sure what -// the flags mean. - -#define STBIR_ALPHA_CHANNEL_NONE -1 - -// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will -// use alpha-weighted resampling (effectively premultiplying, resampling, -// then unpremultiplying). -#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) -// The specified alpha channel should be handled as gamma-corrected value even -// when doing sRGB operations. -#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) - -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags); - - -typedef enum -{ - STBIR_EDGE_CLAMP = 1, - STBIR_EDGE_REFLECT = 2, - STBIR_EDGE_WRAP = 3, - STBIR_EDGE_ZERO = 4, -} stbir_edge; - -// This function adds the ability to specify how requests to sample off the edge of the image are handled. -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode); - -////////////////////////////////////////////////////////////////////////////// -// -// Medium-complexity API -// -// This extends the easy-to-use API as follows: -// -// * Alpha-channel can be processed separately -// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE -// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) -// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) -// * Filter can be selected explicitly -// * uint16 image type -// * sRGB colorspace available for all types -// * context parameter for passing to STBIR_MALLOC - -typedef enum -{ - STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios - STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering - STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque - STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline - STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 -} stbir_filter; - -typedef enum -{ - STBIR_COLORSPACE_LINEAR, - STBIR_COLORSPACE_SRGB, - - STBIR_MAX_COLORSPACES, -} stbir_colorspace; - -// The following functions are all identical except for the type of the image data - -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - - - -////////////////////////////////////////////////////////////////////////////// -// -// Full-complexity API -// -// This extends the medium API as follows: -// -// * uint32 image type -// * not typesafe -// * separate filter types for each axis -// * separate edge modes for each axis -// * can specify scale explicitly for subpixel correctness -// * can specify image source tile using texture coordinates - -typedef enum -{ - STBIR_TYPE_UINT8 , - STBIR_TYPE_UINT16, - STBIR_TYPE_UINT32, - STBIR_TYPE_FLOAT , - - STBIR_MAX_TYPES -} stbir_datatype; - -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context); - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, - float x_offset, float y_offset); - -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1); -// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H - - - - - -#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION - -#ifndef STBIR_ASSERT -#include -#define STBIR_ASSERT(x) assert(x) -#endif - -// For memset -#include - -#include - -#ifndef STBIR_MALLOC -#include -// use comma operator to evaluate c, to avoid "unused parameter" warnings -#define STBIR_MALLOC(size,c) ((void)(c), malloc(size)) -#define STBIR_FREE(ptr,c) ((void)(c), free(ptr)) -#endif - -#ifndef _MSC_VER -#ifdef __cplusplus -#define stbir__inline inline -#else -#define stbir__inline -#endif -#else -#define stbir__inline __forceinline -#endif - - -// should produce compiler error if size is wrong -typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBIR__NOTUSED(v) (void)(v) -#else -#define STBIR__NOTUSED(v) (void)sizeof(v) -#endif - -#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) - -#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE -#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM -#endif - -#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE -#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL -#endif - -#ifndef STBIR_PROGRESS_REPORT -#define STBIR_PROGRESS_REPORT(float_0_to_1) -#endif - -#ifndef STBIR_MAX_CHANNELS -#define STBIR_MAX_CHANNELS 64 -#endif - -#if STBIR_MAX_CHANNELS > 65536 -#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." -// because we store the indices in 16-bit variables -#endif - -// This value is added to alpha just before premultiplication to avoid -// zeroing out color values. It is equivalent to 2^-80. If you don't want -// that behavior (it may interfere if you have floating point images with -// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to -// disable it. -#ifndef STBIR_ALPHA_EPSILON -#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) -#endif - - - -#ifdef _MSC_VER -#define STBIR__UNUSED_PARAM(v) (void)(v) -#else -#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) -#endif - -// must match stbir_datatype -static unsigned char stbir__type_size[] = { - 1, // STBIR_TYPE_UINT8 - 2, // STBIR_TYPE_UINT16 - 4, // STBIR_TYPE_UINT32 - 4, // STBIR_TYPE_FLOAT -}; - -// Kernel function centered at 0 -typedef float (stbir__kernel_fn)(float x, float scale); -typedef float (stbir__support_fn)(float scale); - -typedef struct -{ - stbir__kernel_fn* kernel; - stbir__support_fn* support; -} stbir__filter_info; - -// When upsampling, the contributors are which source pixels contribute. -// When downsampling, the contributors are which destination pixels are contributed to. -typedef struct -{ - int n0; // First contributing pixel - int n1; // Last contributing pixel -} stbir__contributors; - -typedef struct -{ - const void* input_data; - int input_w; - int input_h; - int input_stride_bytes; - - void* output_data; - int output_w; - int output_h; - int output_stride_bytes; - - float s0, t0, s1, t1; - - float horizontal_shift; // Units: output pixels - float vertical_shift; // Units: output pixels - float horizontal_scale; - float vertical_scale; - - int channels; - int alpha_channel; - stbir_uint32 flags; - stbir_datatype type; - stbir_filter horizontal_filter; - stbir_filter vertical_filter; - stbir_edge edge_horizontal; - stbir_edge edge_vertical; - stbir_colorspace colorspace; - - stbir__contributors* horizontal_contributors; - float* horizontal_coefficients; - - stbir__contributors* vertical_contributors; - float* vertical_coefficients; - - int decode_buffer_pixels; - float* decode_buffer; - - float* horizontal_buffer; - - // cache these because ceil/floor are inexplicably showing up in profile - int horizontal_coefficient_width; - int vertical_coefficient_width; - int horizontal_filter_pixel_width; - int vertical_filter_pixel_width; - int horizontal_filter_pixel_margin; - int vertical_filter_pixel_margin; - int horizontal_num_contributors; - int vertical_num_contributors; - - int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) - int ring_buffer_num_entries; // Total number of entries in the ring buffer. - int ring_buffer_first_scanline; - int ring_buffer_last_scanline; - int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer - float* ring_buffer; - - float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. - - int horizontal_contributors_size; - int horizontal_coefficients_size; - int vertical_contributors_size; - int vertical_coefficients_size; - int decode_buffer_size; - int horizontal_buffer_size; - int ring_buffer_size; - int encode_buffer_size; -} stbir__info; - - -static const float stbir__max_uint8_as_float = 255.0f; -static const float stbir__max_uint16_as_float = 65535.0f; -static const double stbir__max_uint32_as_float = 4294967295.0; - - -static stbir__inline int stbir__min(int a, int b) -{ - return a < b ? a : b; -} - -static stbir__inline float stbir__saturate(float x) -{ - if (x < 0) - return 0; - - if (x > 1) - return 1; - - return x; -} - -#ifdef STBIR_SATURATE_INT -static stbir__inline stbir_uint8 stbir__saturate8(int x) -{ - if ((unsigned int) x <= 255) - return x; - - if (x < 0) - return 0; - - return 255; -} - -static stbir__inline stbir_uint16 stbir__saturate16(int x) -{ - if ((unsigned int) x <= 65535) - return x; - - if (x < 0) - return 0; - - return 65535; -} -#endif - -static float stbir__srgb_uchar_to_linear_float[256] = { - 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, - 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, - 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, - 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, - 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, - 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, - 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, - 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, - 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, - 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, - 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, - 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, - 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, - 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, - 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, - 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, - 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, - 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, - 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, - 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, - 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, - 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, - 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, - 0.982251f, 0.991102f, 1.0f -}; - -static float stbir__srgb_to_linear(float f) -{ - if (f <= 0.04045f) - return f / 12.92f; - else - return (float)pow((f + 0.055f) / 1.055f, 2.4f); -} - -static float stbir__linear_to_srgb(float f) -{ - if (f <= 0.0031308f) - return f * 12.92f; - else - return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; -} - -#ifndef STBIR_NON_IEEE_FLOAT -// From https://gist.github.com/rygorous/2203834 - -typedef union -{ - stbir_uint32 u; - float f; -} stbir__FP32; - -static const stbir_uint32 fp32_to_srgb8_tab4[104] = { - 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, - 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, - 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, - 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, - 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, - 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, - 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, - 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, - 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, - 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, - 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, - 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, - 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, -}; - -static stbir_uint8 stbir__linear_to_srgb_uchar(float in) -{ - static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps - static const stbir__FP32 minval = { (127-13) << 23 }; - stbir_uint32 tab,bias,scale,t; - stbir__FP32 f; - - // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. - // The tests are carefully written so that NaNs map to 0, same as in the reference - // implementation. - if (!(in > minval.f)) // written this way to catch NaNs - in = minval.f; - if (in > almostone.f) - in = almostone.f; - - // Do the table lookup and unpack bias, scale - f.f = in; - tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; - bias = (tab >> 16) << 9; - scale = tab & 0xffff; - - // Grab next-highest mantissa bits and perform linear interpolation - t = (f.u >> 12) & 0xff; - return (unsigned char) ((bias + scale*t) >> 16); -} - -#else -// sRGB transition values, scaled by 1<<28 -static int stbir__srgb_offset_to_linear_scaled[256] = -{ - 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, - 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, - 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, - 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, - 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, - 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, - 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, - 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, - 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, - 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, - 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, - 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, - 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, - 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, - 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, - 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, - 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, - 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, - 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, - 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, - 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, - 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, - 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, - 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, - 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, - 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, - 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, - 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, - 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, - 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, - 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, - 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, -}; - -static stbir_uint8 stbir__linear_to_srgb_uchar(float f) -{ - int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp - int v = 0; - int i; - - // Refine the guess with a short binary search. - i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - - return (stbir_uint8) v; -} -#endif - -static float stbir__filter_trapezoid(float x, float scale) -{ - float halfscale = scale / 2; - float t = 0.5f + halfscale; - STBIR_ASSERT(scale <= 1); - - x = (float)fabs(x); - - if (x >= t) - return 0; - else - { - float r = 0.5f - halfscale; - if (x <= r) - return 1; - else - return (t - x) / scale; - } -} - -static float stbir__support_trapezoid(float scale) -{ - STBIR_ASSERT(scale <= 1); - return 0.5f + scale / 2; -} - -static float stbir__filter_triangle(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x <= 1.0f) - return 1 - x; - else - return 0; -} - -static float stbir__filter_cubic(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return (4 + x*x*(3*x - 6))/6; - else if (x < 2.0f) - return (8 + x*(-12 + x*(6 - x)))/6; - - return (0.0f); -} - -static float stbir__filter_catmullrom(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return 1 - x*x*(2.5f - 1.5f*x); - else if (x < 2.0f) - return 2 - x*(4 + x*(0.5f*x - 2.5f)); - - return (0.0f); -} - -static float stbir__filter_mitchell(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return (16 + x*x*(21 * x - 36))/18; - else if (x < 2.0f) - return (32 + x*(-60 + x*(36 - 7*x)))/18; - - return (0.0f); -} - -static float stbir__support_zero(float s) -{ - STBIR__UNUSED_PARAM(s); - return 0; -} - -static float stbir__support_one(float s) -{ - STBIR__UNUSED_PARAM(s); - return 1; -} - -static float stbir__support_two(float s) -{ - STBIR__UNUSED_PARAM(s); - return 2; -} - -static stbir__filter_info stbir__filter_info_table[] = { - { NULL, stbir__support_zero }, - { stbir__filter_trapezoid, stbir__support_trapezoid }, - { stbir__filter_triangle, stbir__support_one }, - { stbir__filter_cubic, stbir__support_two }, - { stbir__filter_catmullrom, stbir__support_two }, - { stbir__filter_mitchell, stbir__support_two }, -}; - -stbir__inline static int stbir__use_upsampling(float ratio) -{ - return ratio > 1; -} - -stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) -{ - return stbir__use_upsampling(stbir_info->horizontal_scale); -} - -stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) -{ - return stbir__use_upsampling(stbir_info->vertical_scale); -} - -// This is the maximum number of input samples that can affect an output sample -// with the given filter -static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) -{ - STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); -} - -// This is how much to expand buffers to account for filters seeking outside -// the image boundaries. -static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) -{ - return stbir__get_filter_pixel_width(filter, scale) / 2; -} - -static int stbir__get_coefficient_width(stbir_filter filter, float scale) -{ - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); -} - -static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) -{ - if (stbir__use_upsampling(scale)) - return output_size; - else - return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); -} - -static int stbir__get_total_horizontal_coefficients(stbir__info* info) -{ - return info->horizontal_num_contributors - * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); -} - -static int stbir__get_total_vertical_coefficients(stbir__info* info) -{ - return info->vertical_num_contributors - * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); -} - -static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) -{ - return &contributors[n]; -} - -// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, -// if you change it here change it there too. -static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) -{ - int width = stbir__get_coefficient_width(filter, scale); - return &coefficients[width*n + c]; -} - -static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) -{ - switch (edge) - { - case STBIR_EDGE_ZERO: - return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later - - case STBIR_EDGE_CLAMP: - if (n < 0) - return 0; - - if (n >= max) - return max - 1; - - return n; // NOTREACHED - - case STBIR_EDGE_REFLECT: - { - if (n < 0) - { - if (n < max) - return -n; - else - return max - 1; - } - - if (n >= max) - { - int max2 = max * 2; - if (n >= max2) - return 0; - else - return max2 - n - 1; - } - - return n; // NOTREACHED - } - - case STBIR_EDGE_WRAP: - if (n >= 0) - return (n % max); - else - { - int m = (-n) % max; - - if (m != 0) - m = max - m; - - return (m); - } - // NOTREACHED - - default: - STBIR_ASSERT(!"Unimplemented edge type"); - return 0; - } -} - -stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) -{ - // avoid per-pixel switch - if (n >= 0 && n < max) - return n; - return stbir__edge_wrap_slow(edge, n, max); -} - -// What input pixels contribute to this output pixel? -static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) -{ - float out_pixel_center = (float)n + 0.5f; - float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; - float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; - - float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; - float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; - - *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; - *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); - *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); -} - -// What output pixels does this input pixel contribute to? -static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) -{ - float in_pixel_center = (float)n + 0.5f; - float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; - float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; - - float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; - float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; - - *out_center_of_in = in_pixel_center * scale_ratio - out_shift; - *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); - *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); -} - -static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) -{ - int i; - float total_filter = 0; - float filter_scale; - - STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - - contributor->n0 = in_first_pixel; - contributor->n1 = in_last_pixel; - - STBIR_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - { - float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); - - // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) - if (i == 0 && !coefficient_group[i]) - { - contributor->n0 = ++in_first_pixel; - i--; - continue; - } - - total_filter += coefficient_group[i]; - } - - // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. - // It would be true in exact math but is at best approximately true in floating-point math, - // and it would not make sense to try and put actual bounds on this here because it depends - // on the image aspect ratio which can get pretty extreme. - //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); - - STBIR_ASSERT(total_filter > 0.9); - STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. - - // Make sure the sum of all coefficients is 1. - filter_scale = 1 / total_filter; - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - coefficient_group[i] *= filter_scale; - - for (i = in_last_pixel - in_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; - - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } -} - -static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) -{ - int i; - - STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - - contributor->n0 = out_first_pixel; - contributor->n1 = out_last_pixel; - - STBIR_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= out_last_pixel - out_first_pixel; i++) - { - float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; - float x = out_pixel_center - out_center_of_in; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; - } - - // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. - // It would be true in exact math but is at best approximately true in floating-point math, - // and it would not make sense to try and put actual bounds on this here because it depends - // on the image aspect ratio which can get pretty extreme. - //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); - - for (i = out_last_pixel - out_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; - - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } -} - -static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size) -{ - int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); - int i, j; - int skip; - - for (i = 0; i < output_size; i++) - { - float scale; - float total = 0; - - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - { - float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); - total += coefficient; - } - else if (i < contributors[j].n0) - break; - } - - STBIR_ASSERT(total > 0.9f); - STBIR_ASSERT(total < 1.1f); - - scale = 1 / total; - - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; - else if (i < contributors[j].n0) - break; - } - } - - // Optimize: Skip zero coefficients and contributions outside of image bounds. - // Do this after normalizing because normalization depends on the n0/n1 values. - for (j = 0; j < num_contributors; j++) - { - int range, max, width; - - skip = 0; - while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) - skip++; - - contributors[j].n0 += skip; - - while (contributors[j].n0 < 0) - { - contributors[j].n0++; - skip++; - } - - range = contributors[j].n1 - contributors[j].n0 + 1; - max = stbir__min(num_coefficients, range); - - width = stbir__get_coefficient_width(filter, scale_ratio); - for (i = 0; i < max; i++) - { - if (i + skip >= width) - break; - - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); - } - - continue; - } - - // Using min to avoid writing into invalid pixels. - for (i = 0; i < num_contributors; i++) - contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); -} - -// Each scan line uses the same kernel values so we should calculate the kernel -// values once and then we can use them for every scan line. -static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) -{ - int n; - int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - - if (stbir__use_upsampling(scale_ratio)) - { - float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; - - // Looping through out pixels - for (n = 0; n < total_contributors; n++) - { - float in_center_of_out; // Center of the current out pixel in the in pixel space - int in_first_pixel, in_last_pixel; - - stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - - stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } - } - else - { - float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; - - // Looping through in pixels - for (n = 0; n < total_contributors; n++) - { - float out_center_of_in; // Center of the current out pixel in the in pixel space - int out_first_pixel, out_last_pixel; - int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); - - stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); - - stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } - - stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size); - } -} - -static float* stbir__get_decode_buffer(stbir__info* stbir_info) -{ - // The 0 index of the decode buffer starts after the margin. This makes - // it okay to use negative indexes on the decode buffer. - return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; -} - -#define STBIR__DECODE(type, colorspace) ((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace)) - -static void stbir__decode_scanline(stbir__info* stbir_info, int n) -{ - int c; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int input_w = stbir_info->input_w; - size_t input_stride_bytes = stbir_info->input_stride_bytes; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir_edge edge_horizontal = stbir_info->edge_horizontal; - stbir_edge edge_vertical = stbir_info->edge_vertical; - size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; - const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; - int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; - int decode = STBIR__DECODE(type, colorspace); - - int x = -stbir_info->horizontal_filter_pixel_margin; - - // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, - // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO - if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) - { - for (; x < max_x; x++) - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - return; - } - - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float); - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float)); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float); - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; - } - - break; - - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) - { - int decode_pixel_index = x * channels; - - // If the alpha value is 0 it will clobber the color values. Make sure it's not. - float alpha = decode_buffer[decode_pixel_index + alpha_channel]; -#ifndef STBIR_NO_ALPHA_EPSILON - if (stbir_info->type != STBIR_TYPE_FLOAT) { - alpha += STBIR_ALPHA_EPSILON; - decode_buffer[decode_pixel_index + alpha_channel] = alpha; - } -#endif - for (c = 0; c < channels; c++) - { - if (c == alpha_channel) - continue; - - decode_buffer[decode_pixel_index + c] *= alpha; - } - } - } - - if (edge_horizontal == STBIR_EDGE_ZERO) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - for (x = input_w; x < max_x; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - } -} - -static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) -{ - return &ring_buffer[index * ring_buffer_length]; -} - -static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) -{ - int ring_buffer_index; - float* ring_buffer; - - stbir_info->ring_buffer_last_scanline = n; - - if (stbir_info->ring_buffer_begin_index < 0) - { - ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; - stbir_info->ring_buffer_first_scanline = n; - } - else - { - ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; - STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); - } - - ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); - memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); - - return ring_buffer; -} - - -static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer) -{ - int x, k; - int output_w = stbir_info->output_w; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; - - for (x = 0; x < output_w; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int out_pixel_index = x * channels; - int coefficient_group = coefficient_width * x; - int coefficient_counter = 0; - - STBIR_ASSERT(n1 >= n0); - STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - int c; - STBIR_ASSERT(coefficient != 0); - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - break; - } - } -} - -static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer) -{ - int x, k; - int input_w = stbir_info->input_w; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; - int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; - int max_x = input_w + filter_pixel_margin * 2; - - STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); - - switch (channels) { - case 1: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 1; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - } - break; - - case 2: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 2; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - } - break; - - case 3: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 3; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - } - break; - - case 4: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 4; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - } - break; - - default: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * channels; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int c; - int out_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - } - break; - } -} - -static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); - - // Now resample it into the ring buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - else - stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - - // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. -} - -static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); - - memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); - - // Now resample it into the horizontal buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer); - else - stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer); - - // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. -} - -// Get the specified scan line from the ring buffer. -static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length) -{ - int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries; - return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); -} - - -static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) -{ - int x; - int n; - int num_nonalpha; - stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - float alpha = encode_buffer[pixel_index + alpha_channel]; - float reciprocal_alpha = alpha ? 1.0f / alpha : 0; - - // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb - for (n = 0; n < channels; n++) - if (n != alpha_channel) - encode_buffer[pixel_index + n] *= reciprocal_alpha; - - // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. - // Because we only add it for integer types, it will automatically be discarded on integer - // conversion, so we don't need to subtract it back out (which would be problematic for - // numeric precision reasons). - } - } - - // build a table of all channels that need colorspace correction, so - // we don't perform colorspace correction on channels that don't need it. - for (x = 0, num_nonalpha = 0; x < channels; ++x) - { - if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - { - nonalpha[num_nonalpha++] = (stbir_uint16)x; - } - } - - #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) - #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) - - #ifdef STBIR__SATURATE_INT - #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float )) - #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float)) - #else - #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float ) - #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float) - #endif - - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); - } - - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float); - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((float*)output_buffer)[index] = encode_buffer[index]; - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; - } - break; - - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } -} - -static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n) -{ - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int ring_buffer_entries = stbir_info->ring_buffer_num_entries; - void* output_data = stbir_info->output_data; - float* encode_buffer = stbir_info->encode_buffer; - int decode = STBIR__DECODE(type, colorspace); - int coefficient_width = stbir_info->vertical_coefficient_width; - int coefficient_counter; - int contributor = n; - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - - int n0,n1, output_row_start; - int coefficient_group = coefficient_width * contributor; - - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; - - output_row_start = n * stbir_info->output_stride_bytes; - - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); - - memset(encode_buffer, 0, output_w * sizeof(float) * channels); - - // I tried reblocking this for better cache usage of encode_buffer - // (using x_outer, k, x_inner), but it lost speed. -- stb - - coefficient_counter = 0; - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 1; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - } - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 2; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - } - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 3; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - } - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 4; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; - } - } - break; - default: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * channels; - int c; - for (c = 0; c < channels; c++) - encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; - } - } - break; - } - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); -} - -static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n) -{ - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int ring_buffer_entries = stbir_info->ring_buffer_num_entries; - float* horizontal_buffer = stbir_info->horizontal_buffer; - int coefficient_width = stbir_info->vertical_coefficient_width; - int contributor = n + stbir_info->vertical_filter_pixel_margin; - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1; - - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; - - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); - - for (k = n0; k <= n1; k++) - { - int coefficient_index = k - n0; - int coefficient_group = coefficient_width * contributor; - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - - switch (channels) { - case 1: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 1; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 2; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 3; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 4; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; - - int c; - for (c = 0; c < channels; c++) - ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; - } - break; - } - } -} - -static void stbir__buffer_loop_upsample(stbir__info* stbir_info) -{ - int y; - float scale_ratio = stbir_info->vertical_scale; - float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; - - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); - - for (y = 0; y < stbir_info->output_h; y++) - { - float in_center_of_out = 0; // Center of the current out scanline in the in scanline space - int in_first_scanline = 0, in_last_scanline = 0; - - stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); - - STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); - - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (in_first_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; - } - } - } - - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); - - while (in_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - - // Now all buffers should be ready to write a row of vertical sampling. - stbir__resample_vertical_upsample(stbir_info, y); - - STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); - } -} - -static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) -{ - int output_stride_bytes = stbir_info->output_stride_bytes; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int output_w = stbir_info->output_w; - void* output_data = stbir_info->output_data; - int decode = STBIR__DECODE(type, colorspace); - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) - { - int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; - float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); - STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); - } - - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; - } - } - } -} - -static void stbir__buffer_loop_downsample(stbir__info* stbir_info) -{ - int y; - float scale_ratio = stbir_info->vertical_scale; - int output_h = stbir_info->output_h; - float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; - int pixel_margin = stbir_info->vertical_filter_pixel_margin; - int max_y = stbir_info->input_h + pixel_margin; - - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); - - for (y = -pixel_margin; y < max_y; y++) - { - float out_center_of_in; // Center of the current out scanline in the in scanline space - int out_first_scanline, out_last_scanline; - - stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); - - STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); - - if (out_last_scanline < 0 || out_first_scanline >= output_h) - continue; - - stbir__empty_ring_buffer(stbir_info, out_first_scanline); - - stbir__decode_and_resample_downsample(stbir_info, y); - - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); - - while (out_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - - // Now the horizontal buffer is ready to write to all ring buffer rows. - stbir__resample_vertical_downsample(stbir_info, y); - } - - stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); -} - -static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) -{ - info->input_w = input_w; - info->input_h = input_h; - info->output_w = output_w; - info->output_h = output_h; - info->channels = channels; -} - -static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) -{ - info->s0 = s0; - info->t0 = t0; - info->s1 = s1; - info->t1 = t1; - - if (transform) - { - info->horizontal_scale = transform[0]; - info->vertical_scale = transform[1]; - info->horizontal_shift = transform[2]; - info->vertical_shift = transform[3]; - } - else - { - info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); - info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); - - info->horizontal_shift = s0 * info->output_w / (s1 - s0); - info->vertical_shift = t0 * info->output_h / (t1 - t0); - } -} - -static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) -{ - if (h_filter == 0) - h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - if (v_filter == 0) - v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - info->horizontal_filter = h_filter; - info->vertical_filter = v_filter; -} - -static stbir_uint32 stbir__calculate_memory(stbir__info *info) -{ - int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); - - info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); - info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); - - // One extra entry because floating point precision problems sometimes cause an extra to be necessary. - info->ring_buffer_num_entries = filter_height + 1; - - info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); - info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); - info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); - info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); - info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); - info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); - info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float); - info->encode_buffer_size = info->output_w * info->channels * sizeof(float); - - STBIR_ASSERT(info->horizontal_filter != 0); - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - STBIR_ASSERT(info->vertical_filter != 0); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - - if (stbir__use_height_upsampling(info)) - // The horizontal buffer is for when we're downsampling the height and we - // can't output the result of sampling the decode buffer directly into the - // ring buffers. - info->horizontal_buffer_size = 0; - else - // The encode buffer is to retain precision in the height upsampling method - // and isn't used when height downsampling. - info->encode_buffer_size = 0; - - return info->horizontal_contributors_size + info->horizontal_coefficients_size - + info->vertical_contributors_size + info->vertical_coefficients_size - + info->decode_buffer_size + info->horizontal_buffer_size - + info->ring_buffer_size + info->encode_buffer_size; -} - -static int stbir__resize_allocated(stbir__info *info, - const void* input_data, int input_stride_in_bytes, - void* output_data, int output_stride_in_bytes, - int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, - void* tempmem, size_t tempmem_size_in_bytes) -{ - size_t memory_required = stbir__calculate_memory(info); - - int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; - int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; - -#ifdef STBIR_DEBUG_OVERWRITE_TEST -#define OVERWRITE_ARRAY_SIZE 8 - unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; - - size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; - memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); -#endif - - STBIR_ASSERT(info->channels >= 0); - STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); - - if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) - return 0; - - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - - if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - - if (alpha_channel < 0) - flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; - - if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { - STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); - } - - if (alpha_channel >= info->channels) - return 0; - - STBIR_ASSERT(tempmem); - - if (!tempmem) - return 0; - - STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); - - if (tempmem_size_in_bytes < memory_required) - return 0; - - memset(tempmem, 0, tempmem_size_in_bytes); - - info->input_data = input_data; - info->input_stride_bytes = width_stride_input; - - info->output_data = output_data; - info->output_stride_bytes = width_stride_output; - - info->alpha_channel = alpha_channel; - info->flags = flags; - info->type = type; - info->edge_horizontal = edge_horizontal; - info->edge_vertical = edge_vertical; - info->colorspace = colorspace; - - info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); - - info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); - info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; - -#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) - - info->horizontal_contributors = (stbir__contributors *) tempmem; - info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); - info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); - info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); - info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); - - if (stbir__use_height_upsampling(info)) - { - info->horizontal_buffer = NULL; - info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - else - { - info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); - info->encode_buffer = NULL; - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - -#undef STBIR__NEXT_MEMPTR - - // This signals that the ring buffer is empty - info->ring_buffer_begin_index = -1; - - stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); - stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); - - STBIR_PROGRESS_REPORT(0); - - if (stbir__use_height_upsampling(info)) - stbir__buffer_loop_upsample(info); - else - stbir__buffer_loop_downsample(info); - - STBIR_PROGRESS_REPORT(1); - -#ifdef STBIR_DEBUG_OVERWRITE_TEST - STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); -#endif - - return 1; -} - - -static int stbir__resize_arbitrary( - void *alloc_context, - const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, float *transform, - int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_filter h_filter, stbir_filter v_filter, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) -{ - stbir__info info; - int result; - size_t memory_required; - void* extra_memory; - - stbir__setup(&info, input_w, input_h, output_w, output_h, channels); - stbir__calculate_transform(&info, s0,t0,s1,t1,transform); - stbir__choose_filter(&info, h_filter, v_filter); - memory_required = stbir__calculate_memory(&info); - extra_memory = STBIR_MALLOC(memory_required, alloc_context); - - if (!extra_memory) - return 0; - - result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, - output_data, output_stride_in_bytes, - alpha_channel, flags, type, - edge_horizontal, edge_vertical, - colorspace, extra_memory, memory_required); - - STBIR_FREE(extra_memory, alloc_context); - - return result; -} - -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); -} - -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); -} - -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - - -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, - float x_offset, float y_offset) -{ - float transform[4]; - transform[0] = x_scale; - transform[1] = y_scale; - transform[2] = x_offset; - transform[3] = y_offset; - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -#endif // STB_IMAGE_RESIZE_IMPLEMENTATION - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/src/external/stb_image_resize2.h b/src/external/stb_image_resize2.h new file mode 100644 index 000000000..e0c428246 --- /dev/null +++ b/src/external/stb_image_resize2.h @@ -0,0 +1,10303 @@ +/* stb_image_resize2 - v2.01 - public domain image resizing + + by Jeff Roberts (v2) and Jorge L Rodriguez + http://github.com/nothings/stb + + Can be threaded with the extended API. SSE2, AVX, Neon and WASM SIMD support. Only + scaling and translation is supported, no rotations or shears. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. + + PORTING FROM VERSION 1 + + The API has changed. You can continue to use the old version of stb_image_resize.h, + which is available in the "deprecated/" directory. + + If you're using the old simple-to-use API, porting is straightforward. + (For more advanced APIs, read the documentation.) + + stbir_resize_uint8(): + - call `stbir_resize_uint8_linear`, cast channel count to `stbir_pixel_layout` + + stbir_resize_float(): + - call `stbir_resize_float_linear`, cast channel count to `stbir_pixel_layout` + + stbir_resize_uint8_srgb(): + - function name is unchanged + - cast channel count to `stbir_pixel_layout` + - above is sufficient unless your image has alpha and it's not RGBA/BGRA + - in that case, follow the below instructions for stbir_resize_uint8_srgb_edgemode + + stbir_resize_uint8_srgb_edgemode() + - switch to the "medium complexity" API + - stbir_resize(), very similar API but a few more parameters: + - pixel_layout: cast channel count to `stbir_pixel_layout` + - data_type: STBIR_TYPE_UINT8_SRGB + - edge: unchanged (STBIR_EDGE_WRAP, etc.) + - filter: STBIR_FILTER_DEFAULT + - which channel is alpha is specified in stbir_pixel_layout, see enum for details + + EASY API CALLS: + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation, clamps to edge. + + stbir_resize_uint8_srgb( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + stbir_resize_uint8_linear( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + stbir_resize_float_linear( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + If you pass NULL or zero for the output_pixels, we will allocate the output buffer + for you and return it from the function (free with free() or STBIR_FREE). + As a special case, XX_stride_in_bytes of 0 means packed continuously in memory. + + API LEVELS + There are three levels of API - easy-to-use, medium-complexity and extended-complexity. + + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + MEMORY ALLOCATION + By default, we use malloc and free for memory allocation. To override the + memory allocation, before the implementation #include, add a: + + #define STBIR_MALLOC(size,user_data) ... + #define STBIR_FREE(ptr,user_data) ... + + Each resize makes exactly one call to malloc/free (unless you use the + extended API where you can do one allocation for many resizes). Under + address sanitizer, we do separate allocations to find overread/writes. + + PERFORMANCE + This library was written with an emphasis on performance. When testing + stb_image_resize with RGBA, the fastest mode is STBIR_4CHANNEL with + STBIR_TYPE_UINT8 pixels and CLAMPed edges (which is what many other resize + libs do by default). Also, make sure SIMD is turned on of course (default + for 64-bit targets). Avoid WRAP edge mode if you want the fastest speed. + + This library also comes with profiling built-in. If you define STBIR_PROFILE, + you can use the advanced API and get low-level profiling information by + calling stbir_resize_extended_profile_info() or stbir_resize_split_profile_info() + after a resize. + + SIMD + Most of the routines have optimized SSE2, AVX, NEON and WASM versions. + + On Microsoft compilers, we automatically turn on SIMD for 64-bit x64 and + ARM; for 32-bit x86 and ARM, you select SIMD mode by defining STBIR_SSE2 or + STBIR_NEON. For AVX and AVX2, we auto-select it by detecting the /arch:AVX + or /arch:AVX2 switches. You can also always manually turn SSE2, AVX or AVX2 + support on by defining STBIR_SSE2, STBIR_AVX or STBIR_AVX2. + + On Linux, SSE2 and Neon is on by default for 64-bit x64 or ARM64. For 32-bit, + we select x86 SIMD mode by whether you have -msse2, -mavx or -mavx2 enabled + on the command line. For 32-bit ARM, you must pass -mfpu=neon-vfpv4 for both + clang and GCC, but GCC also requires an additional -mfp16-format=ieee to + automatically enable NEON. + + On x86 platforms, you can also define STBIR_FP16C to turn on FP16C instructions + for converting back and forth to half-floats. This is autoselected when we + are using AVX2. Clang and GCC also require the -mf16c switch. ARM always uses + the built-in half float hardware NEON instructions. + + You can also tell us to use multiply-add instructions with STBIR_USE_FMA. + Because x86 doesn't always have fma, we turn it off by default to maintain + determinism across all platforms. If you don't care about non-FMA determinism + and are willing to restrict yourself to more recent x86 CPUs (around the AVX + timeframe), then fma will give you around a 15% speedup. + + You can force off SIMD in all cases by defining STBIR_NO_SIMD. You can turn + off AVX or AVX2 specifically with STBIR_NO_AVX or STBIR_NO_AVX2. AVX is 10% + to 40% faster, and AVX2 is generally another 12%. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how the alpha + channel of an image is processed. + + When alpha represents transparency, it is important that when combining + colors with filtering, the pixels should not be treated equally; they + should use a weighted average based on their alpha values. For example, + if a pixel is 1% opaque bright green and another pixel is 99% opaque + black and you average them, the average will be 50% opaque, but the + unweighted average and will be a middling green color, while the weighted + average will be nearly black. This means the unweighted version introduced + green energy that didn't exist in the source image. + + (If you want to know why this makes sense, you can work out the math for + the following: consider what happens if you alpha composite a source image + over a fixed color and then average the output, vs. if you average the + source image pixels and then composite that over the same fixed color. + Only the weighted average produces the same result as the ground truth + composite-then-average result.) + + Therefore, it is in general best to "alpha weight" the pixels when applying + filters to them. This essentially means multiplying the colors by the alpha + values before combining them, and then dividing by the alpha value at the + end. + + The computer graphics industry introduced a technique called "premultiplied + alpha" or "associated alpha" in which image colors are stored in image files + already multiplied by their alpha. This saves some math when compositing, + and also avoids the need to divide by the alpha at the end (which is quite + inefficient). However, while premultiplied alpha is common in the movie CGI + industry, it is not commonplace in other industries like videogames, and most + consumer file formats are generally expected to contain not-premultiplied + colors. For example, Photoshop saves PNG files "unpremultiplied", and web + browsers like Chrome and Firefox expect PNG images to be unpremultiplied. + + Note that there are three possibilities that might describe your image + and resize expectation: + + 1. images are not premultiplied, alpha weighting is desired + 2. images are not premultiplied, alpha weighting is not desired + 3. images are premultiplied + + Both case #2 and case #3 require the exact same math: no alpha weighting + should be applied or removed. Only case 1 requires extra math operations; + the other two cases can be handled identically. + + stb_image_resize expects case #1 by default, applying alpha weighting to + images, expecting the input images to be unpremultiplied. This is what the + COLOR+ALPHA buffer types tell the resizer to do. + + When you use the pixel layouts STBIR_RGBA, STBIR_BGRA, STBIR_ARGB, + STBIR_ABGR, STBIR_RX, or STBIR_XR you are telling us that the pixels are + non-premultiplied. In these cases, the resizer will alpha weight the colors + (effectively creating the premultiplied image), do the filtering, and then + convert back to non-premult on exit. + + When you use the pixel layouts STBIR_RGBA_PM, STBIR_RGBA_PM, STBIR_RGBA_PM, + STBIR_RGBA_PM, STBIR_RX_PM or STBIR_XR_PM, you are telling that the pixels + ARE premultiplied. In this case, the resizer doesn't have to do the + premultipling - it can filter directly on the input. This about twice as + fast as the non-premultiplied case, so it's the right option if your data is + already setup correctly. + + When you use the pixel layout STBIR_4CHANNEL or STBIR_2CHANNEL, you are + telling us that there is no channel that represents transparency; it may be + RGB and some unrelated fourth channel that has been stored in the alpha + channel, but it is actually not alpha. No special processing will be + performed. + + The difference between the generic 4 or 2 channel layouts, and the + specialized _PM versions is with the _PM versions you are telling us that + the data *is* alpha, just don't premultiply it. That's important when + using SRGB pixel formats, we need to know where the alpha is, because + it is converted linearly (rather than with the SRGB converters). + + Because alpha weighting produces the same effect as premultiplying, you + even have the option with non-premultiplied inputs to let the resizer + produce a premultiplied output. Because the intially computed alpha-weighted + output image is effectively premultiplied, this is actually more performant + than the normal path which un-premultiplies the output image as a final step. + + Finally, when converting both in and out of non-premulitplied space (for + example, when using STBIR_RGBA), we go to somewhat heroic measures to + ensure that areas with zero alpha value pixels get something reasonable + in the RGB values. If you don't care about the RGB values of zero alpha + pixels, you can call the stbir_set_non_pm_alpha_speed_over_quality() + function - this runs a premultiplied resize about 25% faster. That said, + when you really care about speed, using premultiplied pixels for both in + and out (STBIR_RGBA_PM, etc) much faster than both of these premultiplied + options. + + PIXEL LAYOUT CONVERSION + The resizer can convert from some pixel layouts to others. When using the + stbir_set_pixel_layouts(), you can, for example, specify STBIR_RGBA + on input, and STBIR_ARGB on output, and it will re-organize the channels + during the resize. Currently, you can only convert between two pixel + layouts with the same number of channels. + + DETERMINISM + We commit to being deterministic (from x64 to ARM to scalar to SIMD, etc). + This requires compiling with fast-math off (using at least /fp:precise). + Also, you must turn off fp-contracting (which turns mult+adds into fmas)! + We attempt to do this with pragmas, but with Clang, you usually want to add + -ffp-contract=off to the command line as well. + + For 32-bit x86, you must use SSE and SSE2 codegen for determinism. That is, + if the scalar x87 unit gets used at all, we immediately lose determinism. + On Microsoft Visual Studio 2008 and earlier, from what we can tell there is + no way to be deterministic in 32-bit x86 (some x87 always leaks in, even + with fp:strict). On 32-bit x86 GCC, determinism requires both -msse2 and + -fpmath=sse. + + Note that we will not be deterministic with float data containing NaNs - + the NaNs will propagate differently on different SIMD and platforms. + + If you turn on STBIR_USE_FMA, then we will be deterministic with other + fma targets, but we will differ from non-fma targets (this is unavoidable, + because a fma isn't simply an add with a mult - it also introduces a + rounding difference compared to non-fma instruction sequences. + + FLOAT PIXEL FORMAT RANGE + Any range of values can be used for the non-alpha float data that you pass + in (0 to 1, -1 to 1, whatever). However, if you are inputting float values + but *outputting* bytes or shorts, you must use a range of 0 to 1 so that we + scale back properly. The alpha channel must also be 0 to 1 for any format + that does premultiplication prior to resizing. + + Note also that with float output, using filters with negative lobes, the + output filtered values might go slightly out of range. You can define + STBIR_FLOAT_LOW_CLAMP and/or STBIR_FLOAT_HIGH_CLAMP to specify the range + to clamp to on output, if that's important. + + MAX/MIN SCALE FACTORS + The input pixel resolutions are in integers, and we do the internal pointer + resolution in size_t sized integers. However, the scale ratio from input + resolution to output resolution is calculated in float form. This means + the effective possible scale ratio is limited to 24 bits (or 16 million + to 1). As you get close to the size of the float resolution (again, 16 + million pixels wide or high), you might start seeing float inaccuracy + issues in general in the pipeline. If you have to do extreme resizes, + you can usually do this is multiple stages (using float intermediate + buffers). + + FLIPPED IMAGES + Stride is just the delta from one scanline to the next. This means you can + use a negative stride to handle inverted images (point to the final + scanline and use a negative stride). You can invert the input or output, + using negative strides. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters to + use, you can change the compile-time defaults with: + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are supplied. For a list of supported + filters, see the stbir_filter enum. You can install your own filters by + using the stbir_set_filter_callbacks function. + + PROGRESS + For interactive use with slow resize operations, you can use the the + scanline callbacks in the extended API. It would have to be a *very* large + image resample to need progress though - we're very fast. + + CEIL and FLOOR + In scalar mode, the only functions we use from math.h are ceilf and floorf, + but if you have your own versions, you can define the STBIR_CEILF(v) and + STBIR_FLOORF(v) macros and we'll use them instead. In SIMD, we just use + our own versions. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + FUTURE TODOS + * For polyphase integral filters, we just memcpy the coeffs to dupe + them, but we should indirect and use the same coeff memory. + * Add pixel layout conversions for sensible different channel counts + (maybe, 1->3/4, 3->4, 4->1, 3->1). + * For SIMD encode and decode scanline routines, do any pre-aligning + for bad input/output buffer alignments and pitch? + * For very wide scanlines, we should we do vertical strips to stay within + L2 cache. Maybe do chunks of 1K pixels at a time. There would be + some pixel reconversion, but probably dwarfed by things falling out + of cache. Probably also something possible with alternating between + scattering and gathering at high resize scales? + * Rewrite the coefficient generator to do many at once. + * AVX-512 vertical kernels - worried about downclocking here. + * Convert the reincludes to macros when we know they aren't changing. + * Experiment with pivoting the horizontal and always using the + vertical filters (which are faster, but perhaps not enough to overcome + the pivot cost and the extra memory touches). Need to buffer the whole + image so have to balance memory use. + * Most of our code is internally function pointers, should we compile + all the SIMD stuff always and dynamically dispatch? + + CONTRIBUTORS + Jeff Roberts: 2.0 implementation, optimizations, SIMD + Martins Mozeiko: NEON simd, WASM simd, clang and GCC whisperer. + Fabian Giesen: half float and srgb converters + Sean Barrett: API design, optimizations + Jorge L Rodriguez: Original 1.0 implementation + Aras Pranckevicius: bugfixes for 1.0 + Nathan Reed: warning fixes for 1.0 + + REVISIONS + 2.00 (2022-02-20) mostly new source: new api, optimizations, simd, vertical-first, etc + (2x-5x faster without simd, 4x-12x faster with simd) + (in some cases, 20x to 40x faster - resizing to very small for example) + 0.96 (2019-03-04) fixed warnings + 0.95 (2017-07-23) fixed warnings + 0.94 (2017-03-18) fixed warnings + 0.93 (2017-03-03) fixed bug with certain combinations of heights + 0.92 (2017-01-02) fix integer overflow on large (>2GB) images + 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions + 0.90 (2014-09-17) first released version + + LICENSE + See end of file for license information. +*/ + +#if !defined(STB_IMAGE_RESIZE_DO_HORIZONTALS) && !defined(STB_IMAGE_RESIZE_DO_VERTICALS) && !defined(STB_IMAGE_RESIZE_DO_CODERS) // for internal re-includes + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE2_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE2_H + +#include +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +typedef unsigned __int64 stbir_uint64; +#else +#include +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +typedef uint64_t stbir_uint64; +#endif + +#ifdef _M_IX86_FP +#if ( _M_IX86_FP >= 1 ) +#ifndef STBIR_SSE +#define STBIR_SSE +#endif +#endif +#endif + +#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(_M_AMD64) || defined(__SSE2__) || defined(STBIR_SSE) || defined(STBIR_SSE2) + #ifndef STBIR_SSE2 + #define STBIR_SSE2 + #endif + #if defined(__AVX__) || defined(STBIR_AVX2) + #ifndef STBIR_AVX + #ifndef STBIR_NO_AVX + #define STBIR_AVX + #endif + #endif + #endif + #if defined(__AVX2__) || defined(STBIR_AVX2) + #ifndef STBIR_NO_AVX2 + #ifndef STBIR_AVX2 + #define STBIR_AVX2 + #endif + #if defined( _MSC_VER ) && !defined(__clang__) + #ifndef STBIR_FP16C // FP16C instructions are on all AVX2 cpus, so we can autoselect it here on microsoft - clang needs -m16c + #define STBIR_FP16C + #endif + #endif + #endif + #endif + #ifdef __F16C__ + #ifndef STBIR_FP16C // turn on FP16C instructions if the define is set (for clang and gcc) + #define STBIR_FP16C + #endif + #endif +#endif + +#if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || defined(_M_ARM) || (__ARM_NEON_FP & 4) != 0 && __ARM_FP16_FORMAT_IEEE != 0 +#ifndef STBIR_NEON +#define STBIR_NEON +#endif +#endif + +#if defined(_M_ARM) +#ifdef STBIR_USE_FMA +#undef STBIR_USE_FMA // no FMA for 32-bit arm on MSVC +#endif +#endif + +#if defined(__wasm__) && defined(__wasm_simd128__) +#ifndef STBIR_WASM +#define STBIR_WASM +#endif +#endif + +#ifndef STBIRDEF +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +//// start "header file" /////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * stride is the offset between successive rows of image data +// in memory, in bytes. specify 0 for packed continuously in memory +// * colorspace is linear or sRGB as specified by function name +// * Uses the default filters +// * Uses edge mode clamped +// * returned result is 1 for success or 0 in case of an error. + + +// stbir_pixel_layout specifies: +// number of channels +// order of channels +// whether color is premultiplied by alpha +// for back compatibility, you can cast the old channel count to an stbir_pixel_layout +typedef enum +{ + STBIR_BGR = 0, // 3-chan, with order specified (for channel flipping) + STBIR_1CHANNEL = 1, + STBIR_2CHANNEL = 2, + STBIR_RGB = 3, // 3-chan, with order specified (for channel flipping) + STBIR_RGBA = 4, // alpha formats, alpha is NOT premultiplied into color channels + + STBIR_4CHANNEL = 5, + STBIR_BGRA = 6, + STBIR_ARGB = 7, + STBIR_ABGR = 8, + STBIR_RA = 9, + STBIR_AR = 10, + + STBIR_RGBA_PM = 11, // alpha formats, alpha is premultiplied into color channels + STBIR_BGRA_PM = 12, + STBIR_ARGB_PM = 13, + STBIR_ABGR_PM = 14, + STBIR_RA_PM = 15, + STBIR_AR_PM = 16, +} stbir_pixel_layout; + +//=============================================================== +// Simple-complexity API +// +// If output_pixels is NULL (0), then we will allocate the buffer and return it to you. +//-------------------------------- + +STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); + +STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); + +STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); +//=============================================================== + +//=============================================================== +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Can specify the datatype - U8, U8_SRGB, U16, FLOAT, HALF_FLOAT +// * Edge wrap can selected explicitly +// * Filter can be selected explicitly +//-------------------------------- + +typedef enum +{ + STBIR_EDGE_CLAMP = 0, + STBIR_EDGE_REFLECT = 1, + STBIR_EDGE_WRAP = 2, // this edge mode is slower and uses more memory + STBIR_EDGE_ZERO = 3, +} stbir_edge; + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 + STBIR_FILTER_POINT_SAMPLE = 6, // Simple point sampling + STBIR_FILTER_OTHER = 7, // User callback specified +} stbir_filter; + +typedef enum +{ + STBIR_TYPE_UINT8 = 0, + STBIR_TYPE_UINT8_SRGB = 1, + STBIR_TYPE_UINT8_SRGB_ALPHA = 2, // alpha channel, when present, should also be SRGB (this is very unusual) + STBIR_TYPE_UINT16 = 3, + STBIR_TYPE_FLOAT = 4, + STBIR_TYPE_HALF_FLOAT = 5 +} stbir_datatype; + +// medium api +STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, + stbir_edge edge, stbir_filter filter ); +//=============================================================== + + + +//=============================================================== +// Extended-complexity API +// +// This API exposes all resize functionality. +// +// * Separate filter types for each axis +// * Separate edge modes for each axis +// * Separate input and output data types +// * Can specify regions with subpixel correctness +// * Can specify alpha flags +// * Can specify a memory callback +// * Can specify a callback data type for pixel input and output +// * Can be threaded for a single resize +// * Can be used to resize many frames without recalculating the sampler info +// +// Use this API as follows: +// 1) Call the stbir_resize_init function on a local STBIR_RESIZE structure +// 2) Call any of the stbir_set functions +// 3) Optionally call stbir_build_samplers() if you are going to resample multiple times +// with the same input and output dimensions (like resizing video frames) +// 4) Resample by calling stbir_resize_extended(). +// 5) Call stbir_free_samplers() if you called stbir_build_samplers() +//-------------------------------- + + +// Types: + +// INPUT CALLBACK: this callback is used for input scanlines +typedef void const * stbir_input_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ); + +// OUTPUT CALLBACK: this callback is used for output scanlines +typedef void stbir_output_callback( void const * output_ptr, int num_pixels, int y, void * context ); + +// callbacks for user installed filters +typedef float stbir__kernel_callback( float x, float scale, void * user_data ); // centered at zero +typedef float stbir__support_callback( float scale, void * user_data ); + +// internal structure with precomputed scaling +typedef struct stbir__info stbir__info; + +typedef struct STBIR_RESIZE // use the stbir_resize_init and stbir_override functions to set these values for future compatibility +{ + void * user_data; + void const * input_pixels; + int input_w, input_h; + double input_s0, input_t0, input_s1, input_t1; + stbir_input_callback * input_cb; + void * output_pixels; + int output_w, output_h; + int output_subx, output_suby, output_subw, output_subh; + stbir_output_callback * output_cb; + int input_stride_in_bytes; + int output_stride_in_bytes; + int splits; + int fast_alpha; + int needs_rebuild; + int called_alloc; + stbir_pixel_layout input_pixel_layout_public; + stbir_pixel_layout output_pixel_layout_public; + stbir_datatype input_data_type; + stbir_datatype output_data_type; + stbir_filter horizontal_filter, vertical_filter; + stbir_edge horizontal_edge, vertical_edge; + stbir__kernel_callback * horizontal_filter_kernel; stbir__support_callback * horizontal_filter_support; + stbir__kernel_callback * vertical_filter_kernel; stbir__support_callback * vertical_filter_support; + stbir__info * samplers; +} STBIR_RESIZE; + +// extended complexity api + + +// First off, you must ALWAYS call stbir_resize_init on your resize structure before any of the other calls! +STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize, + const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero + stbir_pixel_layout pixel_layout, stbir_datatype data_type ); + +//=============================================================== +// You can update these parameters any time after resize_init and there is no cost +//-------------------------------- + +STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ); +STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ); // no callbacks by default +STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ); // pass back STBIR_RESIZE* by default +STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes ); + +//=============================================================== + + +//=============================================================== +// If you call any of these functions, you will trigger a sampler rebuild! +//-------------------------------- + +STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ); // sets new buffer layouts +STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ); // CLAMP by default + +STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ); // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default +STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support ); + +STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets both sub-regions (full regions by default) +STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ); // sets input sub-region (full region by default) +STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets output sub-region (full region by default) + +// when inputting AND outputting non-premultiplied alpha pixels, we use a slower but higher quality technique +// that fills the zero alpha pixel's RGB values with something plausible. If you don't care about areas of +// zero alpha, you can call this function to get about a 25% speed improvement for STBIR_RGBA to STBIR_RGBA +// types of resizes. +STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ); +//=============================================================== + + +//=============================================================== +// You can call build_samplers to prebuild all the internal data we need to resample. +// Then, if you call resize_extended many times with the same resize, you only pay the +// cost once. +// If you do call build_samplers, you MUST call free_samplers eventually. +//-------------------------------- + +// This builds the samplers and does one allocation +STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize ); + +// You MUST call this, if you call stbir_build_samplers or stbir_build_samplers_with_splits +STBIRDEF void stbir_free_samplers( STBIR_RESIZE * resize ); +//=============================================================== + + +// And this is the main function to perform the resize synchronously on one thread. +STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize ); + + +//=============================================================== +// Use these functions for multithreading. +// 1) You call stbir_build_samplers_with_splits first on the main thread +// 2) Then stbir_resize_with_split on each thread +// 3) stbir_free_samplers when done on the main thread +//-------------------------------- + +// This will build samplers for threading. +// You can pass in the number of threads you'd like to use (try_splits). +// It returns the number of splits (threads) that you can call it with. +/// It might be less if the image resize can't be split up that many ways. + +STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int try_splits ); + +// This function does a split of the resizing (you call this fuction for each +// split, on multiple threads). A split is a piece of the output resize pixel space. + +// Note that you MUST call stbir_build_samplers_with_splits before stbir_resize_extended_split! + +// Usually, you will always call stbir_resize_split with split_start as the thread_index +// and "1" for the split_count. +// But, if you have a weird situation where you MIGHT want 8 threads, but sometimes +// only 4 threads, you can use 0,2,4,6 for the split_start's and use "2" for the +// split_count each time to turn in into a 4 thread resize. (This is unusual). + +STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count ); +//=============================================================== + + +//=============================================================== +// Pixel Callbacks info: +//-------------------------------- + +// The input callback is super flexible - it calls you with the input address +// (based on the stride and base pointer), it gives you an optional_output +// pointer that you can fill, or you can just return your own pointer into +// your own data. +// +// You can also do conversion from non-supported data types if necessary - in +// this case, you ignore the input_ptr and just use the x and y parameters to +// calculate your own input_ptr based on the size of each non-supported pixel. +// (Something like the third example below.) +// +// You can also install just an input or just an output callback by setting the +// callback that you don't want to zero. +// +// First example, progress: (getting a callback that you can monitor the progress): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// percentage_done = y / input_height; +// return input_ptr; // use buffer from call +// } +// +// Next example, copying: (copy from some other buffer or stream): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// CopyOrStreamData( optional_output, other_data_src, num_pixels * pixel_width_in_bytes ); +// return optional_output; // return the optional buffer that we filled +// } +// +// Third example, input another buffer without copying: (zero-copy from other buffer): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// void * pixels = ( (char*) other_image_base ) + ( y * other_image_stride ) + ( x * other_pixel_width_in_bytes ); +// return pixels; // return pointer to your data without copying +// } +// +// +// The output callback is considerably simpler - it just calls you so that you can dump +// out each scanline. You could even directly copy out to disk if you have a simple format +// like TGA or BMP. You can also convert to other output types here if you want. +// +// Simple example: +// void const * my_output( void * output_ptr, int num_pixels, int y, void * context ) +// { +// percentage_done = y / output_height; +// fwrite( output_ptr, pixel_width_in_bytes, num_pixels, output_file ); +// } +//=============================================================== + + + + +//=============================================================== +// optional built-in profiling API +//-------------------------------- + +#ifdef STBIR_PROFILE + +typedef struct STBIR_PROFILE_INFO +{ + stbir_uint64 total_clocks; + + // how many clocks spent (of total_clocks) in the various resize routines, along with a string description + // there are "resize_count" number of zones + stbir_uint64 clocks[ 8 ]; + char const ** descriptions; + + // count of clocks and descriptions + stbir_uint32 count; +} STBIR_PROFILE_INFO; + +// use after calling stbir_resize_extended (or stbir_build_samplers or stbir_build_samplers_with_splits) +STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize ); + +// use after calling stbir_resize_extended +STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize ); + +// use after calling stbir_resize_extended_split +STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize, int split_start, int split_num ); + +//=============================================================== + +#endif + + +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE2_H + +#if defined(STB_IMAGE_RESIZE_IMPLEMENTATION) || defined(STB_IMAGE_RESIZE2_IMPLEMENTATION) + +#ifndef STBIR_ASSERT +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +#ifndef STBIR_MALLOC +#include +#define STBIR_MALLOC(size,user_data) ((void)(user_data), malloc(size)) +#define STBIR_FREE(ptr,user_data) ((void)(user_data), free(ptr)) +// (we used the comma operator to evaluate user_data, to avoid "unused parameter" warnings) +#endif + +#ifdef _MSC_VER + +#define stbir__inline __forceinline + +#else + +#define stbir__inline __inline__ + +// Clang address sanitizer +#if defined(__has_feature) + #if __has_feature(address_sanitizer) || __has_feature(memory_sanitizer) + #ifndef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__SEPARATE_ALLOCATIONS + #endif + #endif +#endif + +#endif + +// GCC and MSVC +#if defined(__SANITIZE_ADDRESS__) + #ifndef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__SEPARATE_ALLOCATIONS + #endif +#endif + +// Always turn off automatic FMA use - use STBIR_USE_FMA if you want. +// Otherwise, this is a determinism disaster. +#ifndef STBIR_DONT_CHANGE_FP_CONTRACT // override in case you don't want this behavior +#if defined(_MSC_VER) && !defined(__clang__) +#if _MSC_VER > 1200 +#pragma fp_contract(off) +#endif +#elif defined(__GNUC__) && !defined(__clang__) +#pragma GCC optimize("fp-contract=off") +#else +#pragma STDC FP_CONTRACT OFF +#endif +#endif + +#ifdef _MSC_VER +#define STBIR__UNUSED(v) (void)(v) +#else +#define STBIR__UNUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + + +#ifndef STBIR__HEADER_FILENAME +#define STBIR__HEADER_FILENAME "stb_image_resize2.h" +#endif + +// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types +// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible +typedef enum +{ + STBIRI_1CHANNEL = 0, + STBIRI_2CHANNEL = 1, + STBIRI_RGB = 2, + STBIRI_BGR = 3, + STBIRI_4CHANNEL = 4, + + STBIRI_RGBA = 5, + STBIRI_BGRA = 6, + STBIRI_ARGB = 7, + STBIRI_ABGR = 8, + STBIRI_RA = 9, + STBIRI_AR = 10, + + STBIRI_RGBA_PM = 11, + STBIRI_BGRA_PM = 12, + STBIRI_ARGB_PM = 13, + STBIRI_ABGR_PM = 14, + STBIRI_RA_PM = 15, + STBIRI_AR_PM = 16, +} stbir_internal_pixel_layout; + +// define the public pixel layouts to not compile inside the implementation (to avoid accidental use) +#define STBIR_BGR bad_dont_use_in_implementation +#define STBIR_1CHANNEL STBIR_BGR +#define STBIR_2CHANNEL STBIR_BGR +#define STBIR_RGB STBIR_BGR +#define STBIR_RGBA STBIR_BGR +#define STBIR_4CHANNEL STBIR_BGR +#define STBIR_BGRA STBIR_BGR +#define STBIR_ARGB STBIR_BGR +#define STBIR_ABGR STBIR_BGR +#define STBIR_RA STBIR_BGR +#define STBIR_AR STBIR_BGR +#define STBIR_RGBA_PM STBIR_BGR +#define STBIR_BGRA_PM STBIR_BGR +#define STBIR_ARGB_PM STBIR_BGR +#define STBIR_ABGR_PM STBIR_BGR +#define STBIR_RA_PM STBIR_BGR +#define STBIR_AR_PM STBIR_BGR + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1,1,1,2,4,2 // STBIR_TYPE_UINT8,STBIR_TYPE_UINT8_SRGB,STBIR_TYPE_UINT8_SRGB_ALPHA,STBIR_TYPE_UINT16,STBIR_TYPE_FLOAT,STBIR_TYPE_HALF_FLOAT +}; + +// When gathering, the contributors are which source pixels contribute. +// When scattering, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + int lowest; // First sample index for whole filter + int highest; // Last sample index for whole filter + int widest; // widest single set of samples for an output +} stbir__filter_extent_info; + +typedef struct +{ + int n0; // First pixel of decode buffer to write to + int n1; // Last pixel of decode that will be written to + int pixel_offset_for_input; // Pixel offset into input_scanline +} stbir__span; + +typedef struct stbir__scale_info +{ + int input_full_size; + int output_sub_size; + float scale; + float inv_scale; + float pixel_shift; // starting shift in output pixel space (in pixels) + int scale_is_rational; + stbir_uint32 scale_numerator, scale_denominator; +} stbir__scale_info; + +typedef struct +{ + stbir__contributors * contributors; + float* coefficients; + stbir__contributors * gather_prescatter_contributors; + float * gather_prescatter_coefficients; + stbir__scale_info scale_info; + float support; + stbir_filter filter_enum; + stbir__kernel_callback * filter_kernel; + stbir__support_callback * filter_support; + stbir_edge edge; + int coefficient_width; + int filter_pixel_width; + int filter_pixel_margin; + int num_contributors; + int contributors_size; + int coefficients_size; + stbir__filter_extent_info extent_info; + int is_gather; // 0 = scatter, 1 = gather with scale >= 1, 2 = gather with scale < 1 + int gather_prescatter_num_contributors; + int gather_prescatter_coefficient_width; + int gather_prescatter_contributors_size; + int gather_prescatter_coefficients_size; +} stbir__sampler; + +typedef struct +{ + stbir__contributors conservative; + int edge_sizes[2]; // this can be less than filter_pixel_margin, if the filter and scaling falls off + stbir__span spans[2]; // can be two spans, if doing input subrect with clamp mode WRAP +} stbir__extents; + +typedef struct +{ +#ifdef STBIR_PROFILE + union + { + struct { stbir_uint64 total, looping, vertical, horizontal, decode, encode, alpha, unalpha; } named; + stbir_uint64 array[8]; + } profile; + stbir_uint64 * current_zone_excluded_ptr; +#endif + float* decode_buffer; + + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer + int start_output_y, end_output_y; + int start_input_y, end_input_y; // used in scatter only + + #ifdef STBIR__SEPARATE_ALLOCATIONS + float** ring_buffers; // one pointer for each ring buffer + #else + float* ring_buffer; // one big buffer that we index into + #endif + + float* vertical_buffer; + + char no_cache_straddle[64]; +} stbir__per_split_info; + +typedef void stbir__decode_pixels_func( float * decode, int width_times_channels, void const * input ); +typedef void stbir__alpha_weight_func( float * decode_buffer, int width_times_channels ); +typedef void stbir__horizontal_gather_channels_func( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, + stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ); +typedef void stbir__alpha_unweight_func(float * encode_buffer, int width_times_channels ); +typedef void stbir__encode_pixels_func( void * output, int width_times_channels, float const * encode ); + +struct stbir__info +{ +#ifdef STBIR_PROFILE + union + { + struct { stbir_uint64 total, build, alloc, horizontal, vertical, cleanup, pivot; } named; + stbir_uint64 array[7]; + } profile; + stbir_uint64 * current_zone_excluded_ptr; +#endif + stbir__sampler horizontal; + stbir__sampler vertical; + + void const * input_data; + void * output_data; + + int input_stride_bytes; + int output_stride_bytes; + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_num_entries; // Total number of entries in the ring buffer. + + stbir_datatype input_type; + stbir_datatype output_type; + + stbir_input_callback * in_pixels_cb; + void * user_data; + stbir_output_callback * out_pixels_cb; + + stbir__extents scanline_extents; + + void * alloced_mem; + stbir__per_split_info * split_info; // by default 1, but there will be N of these allocated based on the thread init you did + + stbir__decode_pixels_func * decode_pixels; + stbir__alpha_weight_func * alpha_weight; + stbir__horizontal_gather_channels_func * horizontal_gather_channels; + stbir__alpha_unweight_func * alpha_unweight; + stbir__encode_pixels_func * encode_pixels; + + int alloced_total; + int splits; // count of splits + + stbir_internal_pixel_layout input_pixel_layout_internal; + stbir_internal_pixel_layout output_pixel_layout_internal; + + int input_color_and_type; + int offset_x, offset_y; // offset within output_data + int vertical_first; + int channels; + int effective_channels; // same as channels, except on RGBA/ARGB (7), or XA/AX (3) + int alloc_ring_buffer_num_entries; // Number of entries in the ring buffer that will be allocated +}; + + +#define stbir__max_uint8_as_float 255.0f +#define stbir__max_uint16_as_float 65535.0f +#define stbir__max_uint8_as_float_inverted (1.0f/255.0f) +#define stbir__max_uint16_as_float_inverted (1.0f/65535.0f) +#define stbir__small_float ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) + +// min/max friendly +#define STBIR_CLAMP(x, xmin, xmax) do { \ + if ( (x) < (xmin) ) (x) = (xmin); \ + if ( (x) > (xmax) ) (x) = (xmax); \ +} while (0) + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline int stbir__max(int a, int b) +{ + return a > b ? a : b; +} + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +typedef union +{ + unsigned int u; + float f; +} stbir__FP32; + +// From https://gist.github.com/rygorous/2203834 + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir__inline stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + return 0; + if (in > almostone.f) + return 255; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#ifndef STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT +#define STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT 32 // when downsampling and <= 32 scanlines of buffering, use gather. gather used down to 1/8th scaling for 25% win. +#endif + +// restrict pointers for the output pointers +#if defined( _MSC_VER ) && !defined(__clang__) + #define STBIR_STREAMOUT_PTR( star ) star __restrict + #define STBIR_NO_UNROLL( ptr ) __assume(ptr) // this oddly keeps msvc from unrolling a loop +#elif defined( __clang__ ) + #define STBIR_STREAMOUT_PTR( star ) star __restrict__ + #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr)) +#elif defined( __GNUC__ ) + #define STBIR_STREAMOUT_PTR( star ) star __restrict__ + #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr)) +#else + #define STBIR_STREAMOUT_PTR( star ) star + #define STBIR_NO_UNROLL( ptr ) +#endif + +#ifdef STBIR_NO_SIMD // force simd off for whatever reason + +// force simd off overrides everything else, so clear it all + +#ifdef STBIR_SSE2 +#undef STBIR_SSE2 +#endif + +#ifdef STBIR_AVX +#undef STBIR_AVX +#endif + +#ifdef STBIR_NEON +#undef STBIR_NEON +#endif + +#ifdef STBIR_AVX2 +#undef STBIR_AVX2 +#endif + +#ifdef STBIR_FP16C +#undef STBIR_FP16C +#endif + +#ifdef STBIR_WASM +#undef STBIR_WASM +#endif + +#ifdef STBIR_SIMD +#undef STBIR_SIMD +#endif + +#else // STBIR_SIMD + +#ifdef STBIR_SSE2 + #include + + #define stbir__simdf __m128 + #define stbir__simdi __m128i + + #define stbir_simdi_castf( reg ) _mm_castps_si128(reg) + #define stbir_simdf_casti( reg ) _mm_castsi128_ps(reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = _mm_loadu_ps( (float const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = _mm_loadu_si128 ( (stbir__simdi const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = _mm_castps_si128( _mm_load_ss( (float const*)(ptr) )) + #define stbir__simdf_load1z( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values must be zero + #define stbir__simdf_frep4( fvar ) _mm_set_ps1( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = _mm_set_ps1( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = _mm_castpd_ps(_mm_loadh_pd( _mm_castps_pd(reg), (double*)(ptr) )) + + #define stbir__simdf_zeroP() _mm_setzero_ps() + #define stbir__simdf_zero( reg ) (reg) = _mm_setzero_ps() + + #define stbir__simdf_store( ptr, reg ) _mm_storeu_ps( (float*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), reg ) + #define stbir__simdf_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), _mm_castps_si128(reg) ) + #define stbir__simdf_store2h( ptr, reg ) _mm_storeh_pd( (double*)(ptr), _mm_castps_pd(reg) ) + + #define stbir__simdi_store( ptr, reg ) _mm_storeu_si128( (__m128i*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), _mm_castsi128_ps(reg) ) + #define stbir__simdi_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), (reg) ) + + #define stbir__prefetch( ptr ) _mm_prefetch((char*)(ptr), _MM_HINT_T0 ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out2 = _mm_unpacklo_epi8( ireg, zero ); \ + out3 = _mm_unpackhi_epi8( ireg, zero ); \ + out0 = _mm_unpacklo_epi16( out2, zero ); \ + out1 = _mm_unpackhi_epi16( out2, zero ); \ + out2 = _mm_unpacklo_epi16( out3, zero ); \ + out3 = _mm_unpackhi_epi16( out3, zero ); \ + } + +#define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out = _mm_unpacklo_epi8( ireg, zero ); \ + out = _mm_unpacklo_epi16( out, zero ); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out0 = _mm_unpacklo_epi16( ireg, zero ); \ + out1 = _mm_unpackhi_epi16( ireg, zero ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = _mm_cvttps_epi32(f) + #define stbir__simdf_convert_float_to_int( f ) _mm_cvtt_ss2si(f) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),_mm_setzero_ps())))) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())))) + + #define stbir__simdi_to_int( i ) _mm_cvtsi128_si32(i) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = _mm_cvtepi32_ps( ireg ) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = _mm_add_ps( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = _mm_mul_ps( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = _mm_mul_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = _mm_mul_ss( reg, _mm_load_ss( (float const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = _mm_add_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = _mm_add_ss( reg, _mm_load_ss( (float const*)(ptr) ) ) + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd + #include + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_fmadd_ps( mul1, mul2, add ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_fmadd_ss( mul1, mul2, add ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ps( mul, _mm_loadu_ps( (float const*)(ptr) ), add ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ss( mul, _mm_load_ss( (float const*)(ptr) ), add ) + #else + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_add_ps( add, _mm_mul_ps( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_add_ss( add, _mm_mul_ss( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_add_ps( add, _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_add_ss( add, _mm_mul_ss( mul, _mm_load_ss( (float const*)(ptr) ) ) ) + #endif + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = _mm_add_ss( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = _mm_mul_ss( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = _mm_and_ps( reg0, reg1 ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = _mm_or_ps( reg0, reg1 ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = _mm_min_ps( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = _mm_max_ps( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = _mm_min_ss( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = _mm_max_ss( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (3<<0) + (0<<2) + (1<<4) + (2<<6) ) ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (2<<0) + (3<<2) + (0<<4) + (1<<6) ) ) + + static const stbir__simdf STBIR_zeroones = { 0.0f,1.0f,0.0f,1.0f }; + static const stbir__simdf STBIR_onezeros = { 1.0f,0.0f,1.0f,0.0f }; + #define stbir__simdf_aaa1( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movehl_ps( ones, alp ) ), (1<<0) + (1<<2) + (1<<4) + (2<<6) ) ) + #define stbir__simdf_1aaa( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movelh_ps( ones, alp ) ), (0<<0) + (2<<2) + (2<<4) + (2<<6) ) ) + #define stbir__simdf_a1a1( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_srli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_zeroones ) + #define stbir__simdf_1a1a( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_slli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_onezeros ) + + #define stbir__simdf_swiz( reg, one, two, three, four ) _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( reg ), (one<<0) + (two<<2) + (three<<4) + (four<<6) ) ) + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = _mm_and_si128( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = _mm_or_si128( reg0, reg1 ) + #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = _mm_madd_epi16( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + stbir__simdf af,bf; \ + stbir__simdi a,b; \ + af = _mm_min_ps( aa, STBIR_max_uint8_as_float ); \ + bf = _mm_min_ps( bb, STBIR_max_uint8_as_float ); \ + af = _mm_max_ps( af, _mm_setzero_ps() ); \ + bf = _mm_max_ps( bf, _mm_setzero_ps() ); \ + a = _mm_cvttps_epi32( af ); \ + b = _mm_cvttps_epi32( bf ); \ + a = _mm_packs_epi32( a, b ); \ + out = _mm_packus_epi16( a, a ); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + stbir__simdf_load( o0, (ptr) ); \ + stbir__simdf_load( o1, (ptr)+4 ); \ + stbir__simdf_load( o2, (ptr)+8 ); \ + stbir__simdf_load( o3, (ptr)+12 ); \ + { \ + __m128 tmp0, tmp1, tmp2, tmp3; \ + tmp0 = _mm_unpacklo_ps(o0, o1); \ + tmp2 = _mm_unpacklo_ps(o2, o3); \ + tmp1 = _mm_unpackhi_ps(o0, o1); \ + tmp3 = _mm_unpackhi_ps(o2, o3); \ + o0 = _mm_movelh_ps(tmp0, tmp2); \ + o1 = _mm_movehl_ps(tmp2, tmp0); \ + o2 = _mm_movelh_ps(tmp1, tmp3); \ + o3 = _mm_movehl_ps(tmp3, tmp1); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + r0 = _mm_packs_epi32( r0, r1 ); \ + r2 = _mm_packs_epi32( r2, r3 ); \ + r1 = _mm_unpacklo_epi16( r0, r2 ); \ + r3 = _mm_unpackhi_epi16( r0, r2 ); \ + r0 = _mm_unpacklo_epi16( r1, r3 ); \ + r2 = _mm_unpackhi_epi16( r1, r3 ); \ + r0 = _mm_packus_epi16( r0, r2 ); \ + stbir__simdi_store( ptr, r0 ); \ + + #define stbir__simdi_32shr( out, reg, imm ) out = _mm_srli_epi32( reg, imm ) + + #if defined(_MSC_VER) && !defined(__clang__) + // msvc inits with 8 bytes + #define STBIR__CONST_32_TO_8( v ) (char)(unsigned char)((v)&255),(char)(unsigned char)(((v)>>8)&255),(char)(unsigned char)(((v)>>16)&255),(char)(unsigned char)(((v)>>24)&255) + #define STBIR__CONST_4_32i( v ) STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ) + #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) STBIR__CONST_32_TO_8( v0 ), STBIR__CONST_32_TO_8( v1 ), STBIR__CONST_32_TO_8( v2 ), STBIR__CONST_32_TO_8( v3 ) + #else + // everything else inits with long long's + #define STBIR__CONST_4_32i( v ) (long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v))),(long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v))) + #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) (long long)((((stbir_uint64)(stbir_uint32)(v1))<<32)|((stbir_uint64)(stbir_uint32)(v0))),(long long)((((stbir_uint64)(stbir_uint32)(v3))<<32)|((stbir_uint64)(stbir_uint32)(v2))) + #endif + + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { STBIR__CONST_4_32i(x) } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + + #if defined(STBIR_AVX) || defined(__SSE4_1__) + #include + #define stbir__simdf_pack_to_8words(out,reg0,reg1) out = _mm_packus_epi32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())), _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps()))) + #else + STBIR__SIMDI_CONST(stbir__s32_32768, 32768); + STBIR__SIMDI_CONST(stbir__s16_32768, ((32768<<16)|32768)); + + #define stbir__simdf_pack_to_8words(out,reg0,reg1) \ + { \ + stbir__simdi tmp0,tmp1; \ + tmp0 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \ + tmp1 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \ + tmp0 = _mm_sub_epi32( tmp0, stbir__s32_32768 ); \ + tmp1 = _mm_sub_epi32( tmp1, stbir__s32_32768 ); \ + out = _mm_packs_epi32( tmp0, tmp1 ); \ + out = _mm_sub_epi16( out, stbir__s16_32768 ); \ + } + + #endif + + #define STBIR_SIMD + + // if we detect AVX, set the simd8 defines + #ifdef STBIR_AVX + #include + #define STBIR_SIMD8 + #define stbir__simdf8 __m256 + #define stbir__simdi8 __m256i + #define stbir__simdf8_load( out, ptr ) (out) = _mm256_loadu_ps( (float const *)(ptr) ) + #define stbir__simdi8_load( out, ptr ) (out) = _mm256_loadu_si256( (__m256i const *)(ptr) ) + #define stbir__simdf8_mult( out, a, b ) (out) = _mm256_mul_ps( (a), (b) ) + #define stbir__simdf8_store( ptr, out ) _mm256_storeu_ps( (float*)(ptr), out ) + #define stbir__simdi8_store( ptr, reg ) _mm256_storeu_si256( (__m256i*)(ptr), reg ) + #define stbir__simdf8_frep8( fval ) _mm256_set1_ps( fval ) + + #define stbir__simdf8_min( out, reg0, reg1 ) (out) = _mm256_min_ps( reg0, reg1 ) + #define stbir__simdf8_max( out, reg0, reg1 ) (out) = _mm256_max_ps( reg0, reg1 ) + + #define stbir__simdf8_add4halves( out, bot4, top8 ) (out) = _mm_add_ps( bot4, _mm256_extractf128_ps( top8, 1 ) ) + #define stbir__simdf8_mult_mem( out, reg, ptr ) (out) = _mm256_mul_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf8_add_mem( out, reg, ptr ) (out) = _mm256_add_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf8_add( out, a, b ) (out) = _mm256_add_ps( a, b ) + #define stbir__simdf8_load1b( out, ptr ) (out) = _mm256_broadcast_ss( ptr ) + #define stbir__simdf_load1rep4( out, ptr ) (out) = _mm_broadcast_ss( ptr ) // avx load instruction + + #define stbir__simdi8_convert_i32_to_float(out, ireg) (out) = _mm256_cvtepi32_ps( ireg ) + #define stbir__simdf8_convert_float_to_i32( i, f ) (i) = _mm256_cvttps_epi32(f) + + #define stbir__simdf8_bot4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (0<<0)+(2<<4) ) + #define stbir__simdf8_top4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (1<<0)+(3<<4) ) + + #define stbir__simdf8_gettop4( reg ) _mm256_extractf128_ps(reg,1) + + #ifdef STBIR_AVX2 + + #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi8 a, zero =_mm256_setzero_si256();\ + a = _mm256_permute4x64_epi64( _mm256_unpacklo_epi8( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), zero ),(0<<0)+(2<<2)+(1<<4)+(3<<6)); \ + out0 = _mm256_unpacklo_epi16( a, zero ); \ + out1 = _mm256_unpackhi_epi16( a, zero ); \ + } + + #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \ + { \ + stbir__simdi8 t; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + t = _mm256_permute4x64_epi64( _mm256_packs_epi32( a, b ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \ + out = _mm256_castsi256_si128( _mm256_permute4x64_epi64( _mm256_packus_epi16( t, t ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ) ); \ + } + + #define stbir__simdi8_expand_u16_to_u32(out,ireg) out = _mm256_unpacklo_epi16( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), _mm256_setzero_si256() ); + + #define stbir__simdf8_pack_to_16words(out,aa,bb) \ + { \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + (out) = _mm256_permute4x64_epi64( _mm256_packus_epi32(a, b), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \ + } + + #else + + #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi a,zero = _mm_setzero_si128(); \ + a = _mm_unpacklo_epi8( ireg, zero ); \ + out0 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \ + a = _mm_unpackhi_epi8( ireg, zero ); \ + out1 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \ + } + + #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \ + { \ + stbir__simdi t; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + out = _mm_packs_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \ + out = _mm_packus_epi16( out, out ); \ + t = _mm_packs_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \ + t = _mm_packus_epi16( t, t ); \ + out = _mm_castps_si128( _mm_shuffle_ps( _mm_castsi128_ps(out), _mm_castsi128_ps(t), (0<<0)+(1<<2)+(0<<4)+(1<<6) ) ); \ + } + + #define stbir__simdi8_expand_u16_to_u32(out,ireg) \ + { \ + stbir__simdi a,b,zero = _mm_setzero_si128(); \ + a = _mm_unpacklo_epi16( ireg, zero ); \ + b = _mm_unpackhi_epi16( ireg, zero ); \ + out = _mm256_insertf128_si256( _mm256_castsi128_si256( a ), b, 1 ); \ + } + + #define stbir__simdf8_pack_to_16words(out,aa,bb) \ + { \ + stbir__simdi t0,t1; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + t0 = _mm_packus_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \ + t1 = _mm_packus_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \ + out = _mm256_setr_m128i( t0, t1 ); \ + } + + #endif + + static __m256i stbir_00001111 = { STBIR__CONST_4d_32i( 0, 0, 0, 0 ), STBIR__CONST_4d_32i( 1, 1, 1, 1 ) }; + #define stbir__simdf8_0123to00001111( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00001111 ) + + static __m256i stbir_22223333 = { STBIR__CONST_4d_32i( 2, 2, 2, 2 ), STBIR__CONST_4d_32i( 3, 3, 3, 3 ) }; + #define stbir__simdf8_0123to22223333( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_22223333 ) + + #define stbir__simdf8_0123to2222( out, in ) (out) = stbir__simdf_swiz(_mm256_castps256_ps128(in), 2,2,2,2 ) + + #define stbir__simdf8_load2( out, ptr ) (out) = _mm256_castsi256_ps(_mm256_castsi128_si256( _mm_loadl_epi64( (__m128i*)(ptr)) )) // top values can be random (not denormal or nan for perf) + #define stbir__simdf8_load4b( out, ptr ) (out) = _mm256_broadcast_ps( (__m128 const *)(ptr) ) + + static __m256i stbir_00112233 = { STBIR__CONST_4d_32i( 0, 0, 1, 1 ), STBIR__CONST_4d_32i( 2, 2, 3, 3 ) }; + #define stbir__simdf8_0123to00112233( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00112233 ) + #define stbir__simdf8_add4( out, a8, b ) (out) = _mm256_add_ps( a8, _mm256_castps128_ps256( b ) ) + + static __m256i stbir_load6 = { STBIR__CONST_4_32i( 0x80000000 ), STBIR__CONST_4d_32i( 0x80000000, 0x80000000, 0, 0 ) }; + #define stbir__simdf8_load6z( out, ptr ) (out) = _mm256_maskload_ps( ptr, stbir_load6 ) + + #define stbir__simdf8_0123to00000000( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(0<<4)+(0<<6) ) + #define stbir__simdf8_0123to11111111( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(1<<4)+(1<<6) ) + #define stbir__simdf8_0123to22222222( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(2<<2)+(2<<4)+(2<<6) ) + #define stbir__simdf8_0123to33333333( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(3<<2)+(3<<4)+(3<<6) ) + #define stbir__simdf8_0123to21032103( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(1<<2)+(0<<4)+(3<<6) ) + #define stbir__simdf8_0123to32103210( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(2<<2)+(1<<4)+(0<<6) ) + #define stbir__simdf8_0123to12301230( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(2<<2)+(3<<4)+(0<<6) ) + #define stbir__simdf8_0123to10321032( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(0<<2)+(3<<4)+(2<<6) ) + #define stbir__simdf8_0123to30123012( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(0<<2)+(1<<4)+(2<<6) ) + + #define stbir__simdf8_0123to11331133( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(3<<4)+(3<<6) ) + #define stbir__simdf8_0123to00220022( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(2<<4)+(2<<6) ) + + #define stbir__simdf8_aaa1( out, alp, ones ) (out) = _mm256_blend_ps( alp, ones, (1<<0)+(1<<1)+(1<<2)+(0<<3)+(1<<4)+(1<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (3<<0) + (3<<2) + (3<<4) + (0<<6) ) + #define stbir__simdf8_1aaa( out, alp, ones ) (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(1<<2)+(1<<3)+(0<<4)+(1<<5)+(1<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (0<<4) + (0<<6) ) + #define stbir__simdf8_a1a1( out, alp, ones) (out) = _mm256_blend_ps( alp, ones, (1<<0)+(0<<1)+(1<<2)+(0<<3)+(1<<4)+(0<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ) + #define stbir__simdf8_1a1a( out, alp, ones) (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(0<<2)+(1<<3)+(0<<4)+(1<<5)+(0<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ) + + #define stbir__simdf8_zero( reg ) (reg) = _mm256_setzero_ps() + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd + #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_fmadd_ps( mul1, mul2, add ) + #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_fmadd_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ), add ) + #define stbir__simdf8_madd_mem4( out, add, mul, ptr ) (out) = _mm256_fmadd_ps( _mm256_castps128_ps256( mul ), _mm256_castps128_ps256( _mm_loadu_ps( (float const*)(ptr) ) ), add ) + #else + #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul1, mul2 ) ) + #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ) ) ) + #define stbir__simdf8_madd_mem4( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_castps128_ps256( _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ) ) ) + #endif + #define stbir__if_simdf8_cast_to_simdf4( val ) _mm256_castps256_ps128( val ) + + #endif + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) // martins floorf + { + #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41) + __m128 t = _mm_set_ss(x); + return _mm_cvtss_f32( _mm_floor_ss(t, t) ); + #else + __m128 f = _mm_set_ss(x); + __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f)); + __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(f, t), _mm_set_ss(-1.0f))); + return _mm_cvtss_f32(r); + #endif + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) // martins ceilf + { + #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41) + __m128 t = _mm_set_ss(x); + return _mm_cvtss_f32( _mm_ceil_ss(t, t) ); + #else + __m128 f = _mm_set_ss(x); + __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f)); + __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(t, f), _mm_set_ss(1.0f))); + return _mm_cvtss_f32(r); + #endif + } + +#elif defined(STBIR_NEON) + + #include + + #define stbir__simdf float32x4_t + #define stbir__simdi uint32x4_t + + #define stbir_simdi_castf( reg ) vreinterpretq_u32_f32(reg) + #define stbir_simdf_casti( reg ) vreinterpretq_f32_u32(reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = vld1q_f32( (float const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = vld1q_u32( (uint32_t const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = vld1q_dup_f32( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = vld1q_dup_u32( (uint32_t const*)(ptr) ) + #define stbir__simdf_load1z( out, ptr ) (out) = vld1q_lane_f32( (float const*)(ptr), vdupq_n_f32(0), 0 ) // top values must be zero + #define stbir__simdf_frep4( fvar ) vdupq_n_f32( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = vdupq_n_f32( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = vcombine_f32( vget_low_f32(reg), vld1_f32( (float const*)(ptr) ) ) + + #define stbir__simdf_zeroP() vdupq_n_f32(0) + #define stbir__simdf_zero( reg ) (reg) = vdupq_n_f32(0) + + #define stbir__simdf_store( ptr, reg ) vst1q_f32( (float*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) vst1q_lane_f32( (float*)(ptr), reg, 0) + #define stbir__simdf_store2( ptr, reg ) vst1_f32( (float*)(ptr), vget_low_f32(reg) ) + #define stbir__simdf_store2h( ptr, reg ) vst1_f32( (float*)(ptr), vget_high_f32(reg) ) + + #define stbir__simdi_store( ptr, reg ) vst1q_u32( (uint32_t*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) vst1q_lane_u32( (uint32_t*)(ptr), reg, 0 ) + #define stbir__simdi_store2( ptr, reg ) vst1_u32( (uint32_t*)(ptr), vget_low_u32(reg) ) + + #define stbir__prefetch( ptr ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + uint16x8_t l = vmovl_u8( vget_low_u8 ( vreinterpretq_u8_u32(ireg) ) ); \ + uint16x8_t h = vmovl_u8( vget_high_u8( vreinterpretq_u8_u32(ireg) ) ); \ + out0 = vmovl_u16( vget_low_u16 ( l ) ); \ + out1 = vmovl_u16( vget_high_u16( l ) ); \ + out2 = vmovl_u16( vget_low_u16 ( h ) ); \ + out3 = vmovl_u16( vget_high_u16( h ) ); \ + } + + #define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + uint16x8_t tmp = vmovl_u8( vget_low_u8( vreinterpretq_u8_u32(ireg) ) ); \ + out = vmovl_u16( vget_low_u16( tmp ) ); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + uint16x8_t tmp = vreinterpretq_u16_u32(ireg); \ + out0 = vmovl_u16( vget_low_u16 ( tmp ) ); \ + out1 = vmovl_u16( vget_high_u16( tmp ) ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = vreinterpretq_u32_s32( vcvtq_s32_f32(f) ) + #define stbir__simdf_convert_float_to_int( f ) vgetq_lane_s32(vcvtq_s32_f32(f), 0) + #define stbir__simdi_to_int( i ) (int)vgetq_lane_u32(i, 0) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),vdupq_n_f32(0))), 0)) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),vdupq_n_f32(0))), 0)) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = vcvtq_f32_s32( vreinterpretq_s32_u32(ireg) ) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) ) + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd (and also x64 no madd to arm madd) + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_dup_f32( (float const*)(ptr) ) ) + #else + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_f32( (float const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_dup_f32( (float const*)(ptr) ) ) ) + #endif + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 3 ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 2 ) + + #define stbir__simdf_a1a1( out, alp, ones ) (out) = vzipq_f32(vuzpq_f32(alp, alp).val[1], ones).val[0] + #define stbir__simdf_1a1a( out, alp, ones ) (out) = vzipq_f32(ones, vuzpq_f32(alp, alp).val[0]).val[0] + + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + + #define stbir__simdf_aaa1( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3, ones, 3) + #define stbir__simdf_1aaa( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0, ones, 0) + + #if defined( _MSC_VER ) && !defined(__clang__) + #define stbir_make16(a,b,c,d) vcombine_u8( \ + vcreate_u8( (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \ + ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56)), \ + vcreate_u8( (4*c+0) | ((4*c+1)<<8) | ((4*c+2)<<16) | ((4*c+3)<<24) | \ + ((stbir_uint64)(4*d+0)<<32) | ((stbir_uint64)(4*d+1)<<40) | ((stbir_uint64)(4*d+2)<<48) | ((stbir_uint64)(4*d+3)<<56) ) ) + #else + #define stbir_make16(a,b,c,d) (uint8x16_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3,4*c+0,4*c+1,4*c+2,4*c+3,4*d+0,4*d+1,4*d+2,4*d+3} + #endif + + #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vqtbl1q_u8( vreinterpretq_u8_f32(reg), stbir_make16(one, two, three, four) ) ) + + #define stbir__simdi_16madd( out, reg0, reg1 ) \ + { \ + int16x8_t r0 = vreinterpretq_s16_u32(reg0); \ + int16x8_t r1 = vreinterpretq_s16_u32(reg1); \ + int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \ + int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \ + (out) = vreinterpretq_u32_s32( vpaddq_s32(tmp0, tmp1) ); \ + } + + #else + + #define stbir__simdf_aaa1( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3) + #define stbir__simdf_1aaa( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0) + + #if defined( _MSC_VER ) && !defined(__clang__) + static stbir__inline uint8x8x2_t stbir_make8x2(float32x4_t reg) + { + uint8x8x2_t r = { { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } }; + return r; + } + #define stbir_make8(a,b) vcreate_u8( \ + (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \ + ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56) ) + #else + #define stbir_make8x2(reg) (uint8x8x2_t){ { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } } + #define stbir_make8(a,b) (uint8x8_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3} + #endif + + #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vcombine_u8( \ + vtbl2_u8( stbir_make8x2( reg ), stbir_make8( one, two ) ), \ + vtbl2_u8( stbir_make8x2( reg ), stbir_make8( three, four ) ) ) ) + + #define stbir__simdi_16madd( out, reg0, reg1 ) \ + { \ + int16x8_t r0 = vreinterpretq_s16_u32(reg0); \ + int16x8_t r1 = vreinterpretq_s16_u32(reg1); \ + int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \ + int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \ + int32x2_t out0 = vpadd_s32( vget_low_s32(tmp0), vget_high_s32(tmp0) ); \ + int32x2_t out1 = vpadd_s32( vget_low_s32(tmp1), vget_high_s32(tmp1) ); \ + (out) = vreinterpretq_u32_s32( vcombine_s32(out0, out1) ); \ + } + + #endif + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = vandq_u32( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = vorrq_u32( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \ + float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \ + int16x4_t ai = vqmovn_s32( vcvtq_s32_f32( af ) ); \ + int16x4_t bi = vqmovn_s32( vcvtq_s32_f32( bf ) ); \ + uint8x8_t out8 = vqmovun_s16( vcombine_s16(ai, bi) ); \ + out = vreinterpretq_u32_u8( vcombine_u8(out8, out8) ); \ + } + + #define stbir__simdf_pack_to_8words(out,aa,bb) \ + { \ + float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \ + float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \ + int32x4_t ai = vcvtq_s32_f32( af ); \ + int32x4_t bi = vcvtq_s32_f32( bf ); \ + out = vreinterpretq_u32_u16( vcombine_u16(vqmovun_s32(ai), vqmovun_s32(bi)) ); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + { \ + int16x4x2_t tmp0 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r0)), vqmovn_s32(vreinterpretq_s32_u32(r2)) ); \ + int16x4x2_t tmp1 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r1)), vqmovn_s32(vreinterpretq_s32_u32(r3)) ); \ + uint8x8x2_t out = \ + { { \ + vqmovun_s16( vcombine_s16(tmp0.val[0], tmp0.val[1]) ), \ + vqmovun_s16( vcombine_s16(tmp1.val[0], tmp1.val[1]) ), \ + } }; \ + vst2_u8(ptr, out); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + { \ + float32x4x4_t tmp = vld4q_f32(ptr); \ + o0 = tmp.val[0]; \ + o1 = tmp.val[1]; \ + o2 = tmp.val[2]; \ + o3 = tmp.val[3]; \ + } + + #define stbir__simdi_32shr( out, reg, imm ) out = vshrq_n_u32( reg, imm ) + + #if defined( _MSC_VER ) && !defined(__clang__) + #define STBIR__SIMDF_CONST(var, x) __declspec(align(8)) float var[] = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) __declspec(align(8)) uint32_t var[] = { x, x, x, x } + #define STBIR__CONSTF(var) (*(const float32x4_t*)var) + #define STBIR__CONSTI(var) (*(const uint32x4_t*)var) + #else + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + #endif + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) + { + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + return vget_lane_f32( vrndm_f32( vdup_n_f32(x) ), 0); + #else + float32x2_t f = vdup_n_f32(x); + float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f)); + uint32x2_t a = vclt_f32(f, t); + uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(-1.0f)); + float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b))); + return vget_lane_f32(r, 0); + #endif + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) + { + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + return vget_lane_f32( vrndp_f32( vdup_n_f32(x) ), 0); + #else + float32x2_t f = vdup_n_f32(x); + float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f)); + uint32x2_t a = vclt_f32(t, f); + uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(1.0f)); + float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b))); + return vget_lane_f32(r, 0); + #endif + } + + #define STBIR_SIMD + +#elif defined(STBIR_WASM) + + #include + + #define stbir__simdf v128_t + #define stbir__simdi v128_t + + #define stbir_simdi_castf( reg ) (reg) + #define stbir_simdf_casti( reg ) (reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) ) + #define stbir__simdf_load1z( out, ptr ) (out) = wasm_v128_load32_zero( (void const*)(ptr) ) // top values must be zero + #define stbir__simdf_frep4( fvar ) wasm_f32x4_splat( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = wasm_f32x4_splat( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = wasm_v128_load64_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = wasm_v128_load64_zero( (void const*)(ptr) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = wasm_v128_load64_lane( (void const*)(ptr), reg, 1 ) + + #define stbir__simdf_zeroP() wasm_f32x4_const_splat(0) + #define stbir__simdf_zero( reg ) (reg) = wasm_f32x4_const_splat(0) + + #define stbir__simdf_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdf_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdf_store2h( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 1 ) + + #define stbir__simdi_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdi_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 ) + + #define stbir__prefetch( ptr ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + v128_t l = wasm_u16x8_extend_low_u8x16 ( ireg ); \ + v128_t h = wasm_u16x8_extend_high_u8x16( ireg ); \ + out0 = wasm_u32x4_extend_low_u16x8 ( l ); \ + out1 = wasm_u32x4_extend_high_u16x8( l ); \ + out2 = wasm_u32x4_extend_low_u16x8 ( h ); \ + out3 = wasm_u32x4_extend_high_u16x8( h ); \ + } + + #define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + v128_t tmp = wasm_u16x8_extend_low_u8x16(ireg); \ + out = wasm_u32x4_extend_low_u16x8(tmp); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + out0 = wasm_u32x4_extend_low_u16x8 ( ireg ); \ + out1 = wasm_u32x4_extend_high_u16x8( ireg ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = wasm_i32x4_trunc_sat_f32x4(f) + #define stbir__simdf_convert_float_to_int( f ) wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(f), 0) + #define stbir__simdi_to_int( i ) wasm_i32x4_extract_lane(i, 0) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint8_as_float),wasm_f32x4_const_splat(0))), 0)) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint16_as_float),wasm_f32x4_const_splat(0))), 0)) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = wasm_f32x4_convert_i32x4(ireg) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load( (void const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load32_splat( (void const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load( (void const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load32_splat( (void const*)(ptr) ) ) + + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load( (void const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load32_splat( (void const*)(ptr) ) ) ) + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 3, 4, 5, -1 ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 2, 3, 4, -1 ) + + #define stbir__simdf_aaa1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 3, 3, 3, 4) + #define stbir__simdf_1aaa(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 0, 0) + #define stbir__simdf_a1a1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 1, 4, 3, 4) + #define stbir__simdf_1a1a(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 4, 2) + + #define stbir__simdf_swiz( reg, one, two, three, four ) wasm_i32x4_shuffle(reg, reg, one, two, three, four) + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 ) + #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = wasm_i32x4_dot_i16x8( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \ + v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \ + v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \ + v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \ + v128_t out16 = wasm_i16x8_narrow_i32x4( ai, bi ); \ + out = wasm_u8x16_narrow_i16x8( out16, out16 ); \ + } + + #define stbir__simdf_pack_to_8words(out,aa,bb) \ + { \ + v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \ + v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \ + v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \ + v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \ + out = wasm_u16x8_narrow_i32x4( ai, bi ); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + { \ + v128_t tmp0 = wasm_i16x8_narrow_i32x4(r0, r1); \ + v128_t tmp1 = wasm_i16x8_narrow_i32x4(r2, r3); \ + v128_t tmp = wasm_u8x16_narrow_i16x8(tmp0, tmp1); \ + tmp = wasm_i8x16_shuffle(tmp, tmp, 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); \ + wasm_v128_store( (void*)(ptr), tmp); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + { \ + v128_t t0 = wasm_v128_load( ptr ); \ + v128_t t1 = wasm_v128_load( ptr+4 ); \ + v128_t t2 = wasm_v128_load( ptr+8 ); \ + v128_t t3 = wasm_v128_load( ptr+12 ); \ + v128_t s0 = wasm_i32x4_shuffle(t0, t1, 0, 4, 2, 6); \ + v128_t s1 = wasm_i32x4_shuffle(t0, t1, 1, 5, 3, 7); \ + v128_t s2 = wasm_i32x4_shuffle(t2, t3, 0, 4, 2, 6); \ + v128_t s3 = wasm_i32x4_shuffle(t2, t3, 1, 5, 3, 7); \ + o0 = wasm_i32x4_shuffle(s0, s2, 0, 1, 4, 5); \ + o1 = wasm_i32x4_shuffle(s1, s3, 0, 1, 4, 5); \ + o2 = wasm_i32x4_shuffle(s0, s2, 2, 3, 6, 7); \ + o3 = wasm_i32x4_shuffle(s1, s3, 2, 3, 6, 7); \ + } + + #define stbir__simdi_32shr( out, reg, imm ) out = wasm_u32x4_shr( reg, imm ) + + typedef float stbir__f32x4 __attribute__((__vector_size__(16), __aligned__(16))); + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = (v128_t)(stbir__f32x4){ x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) + { + return wasm_f32x4_extract_lane( wasm_f32x4_floor( wasm_f32x4_splat(x) ), 0); + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) + { + return wasm_f32x4_extract_lane( wasm_f32x4_ceil( wasm_f32x4_splat(x) ), 0); + } + + #define STBIR_SIMD + +#endif // SSE2/NEON/WASM + +#endif // NO SIMD + +#ifdef STBIR_SIMD8 + #define stbir__simdfX stbir__simdf8 + #define stbir__simdiX stbir__simdi8 + #define stbir__simdfX_load stbir__simdf8_load + #define stbir__simdiX_load stbir__simdi8_load + #define stbir__simdfX_mult stbir__simdf8_mult + #define stbir__simdfX_add_mem stbir__simdf8_add_mem + #define stbir__simdfX_madd_mem stbir__simdf8_madd_mem + #define stbir__simdfX_store stbir__simdf8_store + #define stbir__simdiX_store stbir__simdi8_store + #define stbir__simdf_frepX stbir__simdf8_frep8 + #define stbir__simdfX_madd stbir__simdf8_madd + #define stbir__simdfX_min stbir__simdf8_min + #define stbir__simdfX_max stbir__simdf8_max + #define stbir__simdfX_aaa1 stbir__simdf8_aaa1 + #define stbir__simdfX_1aaa stbir__simdf8_1aaa + #define stbir__simdfX_a1a1 stbir__simdf8_a1a1 + #define stbir__simdfX_1a1a stbir__simdf8_1a1a + #define stbir__simdfX_convert_float_to_i32 stbir__simdf8_convert_float_to_i32 + #define stbir__simdfX_pack_to_words stbir__simdf8_pack_to_16words + #define stbir__simdfX_zero stbir__simdf8_zero + #define STBIR_onesX STBIR_ones8 + #define STBIR_max_uint8_as_floatX STBIR_max_uint8_as_float8 + #define STBIR_max_uint16_as_floatX STBIR_max_uint16_as_float8 + #define STBIR_simd_point5X STBIR_simd_point58 + #define stbir__simdfX_float_count 8 + #define stbir__simdfX_0123to1230 stbir__simdf8_0123to12301230 + #define stbir__simdfX_0123to2103 stbir__simdf8_0123to21032103 + static const stbir__simdf8 STBIR_max_uint16_as_float_inverted8 = { stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted }; + static const stbir__simdf8 STBIR_max_uint8_as_float_inverted8 = { stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted }; + static const stbir__simdf8 STBIR_ones8 = { 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }; + static const stbir__simdf8 STBIR_simd_point58 = { 0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5 }; + static const stbir__simdf8 STBIR_max_uint8_as_float8 = { stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float, stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float }; + static const stbir__simdf8 STBIR_max_uint16_as_float8 = { stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float, stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float }; +#else + #define stbir__simdfX stbir__simdf + #define stbir__simdiX stbir__simdi + #define stbir__simdfX_load stbir__simdf_load + #define stbir__simdiX_load stbir__simdi_load + #define stbir__simdfX_mult stbir__simdf_mult + #define stbir__simdfX_add_mem stbir__simdf_add_mem + #define stbir__simdfX_madd_mem stbir__simdf_madd_mem + #define stbir__simdfX_store stbir__simdf_store + #define stbir__simdiX_store stbir__simdi_store + #define stbir__simdf_frepX stbir__simdf_frep4 + #define stbir__simdfX_madd stbir__simdf_madd + #define stbir__simdfX_min stbir__simdf_min + #define stbir__simdfX_max stbir__simdf_max + #define stbir__simdfX_aaa1 stbir__simdf_aaa1 + #define stbir__simdfX_1aaa stbir__simdf_1aaa + #define stbir__simdfX_a1a1 stbir__simdf_a1a1 + #define stbir__simdfX_1a1a stbir__simdf_1a1a + #define stbir__simdfX_convert_float_to_i32 stbir__simdf_convert_float_to_i32 + #define stbir__simdfX_pack_to_words stbir__simdf_pack_to_8words + #define stbir__simdfX_zero stbir__simdf_zero + #define STBIR_onesX STBIR__CONSTF(STBIR_ones) + #define STBIR_simd_point5X STBIR__CONSTF(STBIR_simd_point5) + #define STBIR_max_uint8_as_floatX STBIR__CONSTF(STBIR_max_uint8_as_float) + #define STBIR_max_uint16_as_floatX STBIR__CONSTF(STBIR_max_uint16_as_float) + #define stbir__simdfX_float_count 4 + #define stbir__if_simdf8_cast_to_simdf4( val ) ( val ) + #define stbir__simdfX_0123to1230 stbir__simdf_0123to1230 + #define stbir__simdfX_0123to2103 stbir__simdf_0123to2103 +#endif + + +#if defined(STBIR_NEON) && !defined(_M_ARM) + + #if defined( _MSC_VER ) && !defined(__clang__) + typedef __int16 stbir__FP16; + #else + typedef float16_t stbir__FP16; + #endif + +#else // no NEON, or 32-bit ARM for MSVC + + typedef union stbir__FP16 + { + unsigned short u; + } stbir__FP16; + +#endif + +#if !defined(STBIR_NEON) && !defined(STBIR_FP16C) || defined(STBIR_NEON) && defined(_M_ARM) + + // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668 + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + static const stbir__FP32 magic = { (254 - 15) << 23 }; + static const stbir__FP32 was_infnan = { (127 + 16) << 23 }; + stbir__FP32 o; + + o.u = (h.u & 0x7fff) << 13; // exponent/mantissa bits + o.f *= magic.f; // exponent adjust + if (o.f >= was_infnan.f) // make sure Inf/NaN survive + o.u |= 255 << 23; + o.u |= (h.u & 0x8000) << 16; // sign bit + return o.f; + } + + static stbir__inline stbir__FP16 stbir__float_to_half(float val) + { + stbir__FP32 f32infty = { 255 << 23 }; + stbir__FP32 f16max = { (127 + 16) << 23 }; + stbir__FP32 denorm_magic = { ((127 - 15) + (23 - 10) + 1) << 23 }; + unsigned int sign_mask = 0x80000000u; + stbir__FP16 o = { 0 }; + stbir__FP32 f; + unsigned int sign; + + f.f = val; + sign = f.u & sign_mask; + f.u ^= sign; + + if (f.u >= f16max.u) // result is Inf or NaN (all exponent bits set) + o.u = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf + else // (De)normalized number or zero + { + if (f.u < (113 << 23)) // resulting FP16 is subnormal or zero + { + // use a magic value to align our 10 mantissa bits at the bottom of + // the float. as long as FP addition is round-to-nearest-even this + // just works. + f.f += denorm_magic.f; + // and one integer subtract of the bias later, we have our final float! + o.u = (unsigned short) ( f.u - denorm_magic.u ); + } + else + { + unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd + // update exponent, rounding bias part 1 + f.u = f.u + ((15u - 127) << 23) + 0xfff; + // rounding bias part 2 + f.u += mant_odd; + // take the bits! + o.u = (unsigned short) ( f.u >> 13 ); + } + } + + o.u |= sign >> 16; + return o; + } + +#endif + + +#if defined(STBIR_FP16C) + + #include + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + _mm256_storeu_ps( (float*)output, _mm256_cvtph_ps( _mm_loadu_si128( (__m128i const* )input ) ) ); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + _mm_storeu_si128( (__m128i*)output, _mm256_cvtps_ph( _mm256_loadu_ps( input ), 0 ) ); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return _mm_cvtss_f32( _mm_cvtph_ps( _mm_cvtsi32_si128( (int)h.u ) ) ); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + stbir__FP16 h; + h.u = (unsigned short) _mm_cvtsi128_si32( _mm_cvtps_ph( _mm_set_ss( f ), 0 ) ); + return h; + } + +#elif defined(STBIR_SSE2) + + // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668 + stbir__inline static void stbir__half_to_float_SIMD(float * output, void const * input) + { + static const STBIR__SIMDI_CONST(mask_nosign, 0x7fff); + static const STBIR__SIMDI_CONST(smallest_normal, 0x0400); + static const STBIR__SIMDI_CONST(infinity, 0x7c00); + static const STBIR__SIMDI_CONST(expadjust_normal, (127 - 15) << 23); + static const STBIR__SIMDI_CONST(magic_denorm, 113 << 23); + + __m128i i = _mm_loadu_si128 ( (__m128i const*)(input) ); + __m128i h = _mm_unpacklo_epi16 ( i, _mm_setzero_si128() ); + __m128i mnosign = STBIR__CONSTI(mask_nosign); + __m128i eadjust = STBIR__CONSTI(expadjust_normal); + __m128i smallest = STBIR__CONSTI(smallest_normal); + __m128i infty = STBIR__CONSTI(infinity); + __m128i expmant = _mm_and_si128(mnosign, h); + __m128i justsign = _mm_xor_si128(h, expmant); + __m128i b_notinfnan = _mm_cmpgt_epi32(infty, expmant); + __m128i b_isdenorm = _mm_cmpgt_epi32(smallest, expmant); + __m128i shifted = _mm_slli_epi32(expmant, 13); + __m128i adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust); + __m128i adjusted = _mm_add_epi32(eadjust, shifted); + __m128i den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm)); + __m128i adjusted2 = _mm_add_epi32(adjusted, adj_infnan); + __m128 den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm); + __m128 adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm)); + __m128 adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2)); + __m128 adjusted5 = _mm_or_ps(adjusted3, adjusted4); + __m128i sign = _mm_slli_epi32(justsign, 16); + __m128 final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign)); + stbir__simdf_store( output + 0, final ); + + h = _mm_unpackhi_epi16 ( i, _mm_setzero_si128() ); + expmant = _mm_and_si128(mnosign, h); + justsign = _mm_xor_si128(h, expmant); + b_notinfnan = _mm_cmpgt_epi32(infty, expmant); + b_isdenorm = _mm_cmpgt_epi32(smallest, expmant); + shifted = _mm_slli_epi32(expmant, 13); + adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust); + adjusted = _mm_add_epi32(eadjust, shifted); + den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm)); + adjusted2 = _mm_add_epi32(adjusted, adj_infnan); + den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm); + adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm)); + adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2)); + adjusted5 = _mm_or_ps(adjusted3, adjusted4); + sign = _mm_slli_epi32(justsign, 16); + final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign)); + stbir__simdf_store( output + 4, final ); + + // ~38 SSE2 ops for 8 values + } + + // Fabian's round-to-nearest-even float to half + // ~48 SSE2 ops for 8 output + stbir__inline static void stbir__float_to_half_SIMD(void * output, float const * input) + { + static const STBIR__SIMDI_CONST(mask_sign, 0x80000000u); + static const STBIR__SIMDI_CONST(c_f16max, (127 + 16) << 23); // all FP32 values >=this round to +inf + static const STBIR__SIMDI_CONST(c_nanbit, 0x200); + static const STBIR__SIMDI_CONST(c_infty_as_fp16, 0x7c00); + static const STBIR__SIMDI_CONST(c_min_normal, (127 - 14) << 23); // smallest FP32 that yields a normalized FP16 + static const STBIR__SIMDI_CONST(c_subnorm_magic, ((127 - 15) + (23 - 10) + 1) << 23); + static const STBIR__SIMDI_CONST(c_normal_bias, 0xfff - ((127 - 15) << 23)); // adjust exponent and add mantissa rounding + + __m128 f = _mm_loadu_ps(input); + __m128 msign = _mm_castsi128_ps(STBIR__CONSTI(mask_sign)); + __m128 justsign = _mm_and_ps(msign, f); + __m128 absf = _mm_xor_ps(f, justsign); + __m128i absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit) + __m128i f16max = STBIR__CONSTI(c_f16max); + __m128 b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN? + __m128i b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special? + __m128i nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), STBIR__CONSTI(c_nanbit)); + __m128i inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials + + __m128i min_normal = STBIR__CONSTI(c_min_normal); + __m128i b_issub = _mm_cmpgt_epi32(min_normal, absf_int); + + // "result is subnormal" path + __m128 subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa + __m128i subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias + + // "result is normal" path + __m128i mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign + __m128i mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0 + + __m128i round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias)); + __m128i round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE) + __m128i normal = _mm_srli_epi32(round2, 13); // rounded result + + // combine the two non-specials + __m128i nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal)); + + // merge in specials as well + __m128i joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan)); + + __m128i sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16); + __m128i final2, final= _mm_or_si128(joined, sign_shift); + + f = _mm_loadu_ps(input+4); + justsign = _mm_and_ps(msign, f); + absf = _mm_xor_ps(f, justsign); + absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit) + b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN? + b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special? + nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), c_nanbit); + inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials + + b_issub = _mm_cmpgt_epi32(min_normal, absf_int); + + // "result is subnormal" path + subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa + subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias + + // "result is normal" path + mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign + mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0 + + round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias)); + round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE) + normal = _mm_srli_epi32(round2, 13); // rounded result + + // combine the two non-specials + nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal)); + + // merge in specials as well + joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan)); + + sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16); + final2 = _mm_or_si128(joined, sign_shift); + final = _mm_packs_epi32(final, final2); + stbir__simdi_store( output,final ); + } + +#elif defined(STBIR_WASM) || (defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM)) // WASM or 32-bit ARM on MSVC/clang + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + for (int i=0; i<8; i++) + { + output[i] = stbir__half_to_float(input[i]); + } + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + for (int i=0; i<8; i++) + { + output[i] = stbir__float_to_half(input[i]); + } + } + +#elif defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) // 64-bit ARM on MSVC (not clang) + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + float16x4_t in0 = vld1_f16(input + 0); + float16x4_t in1 = vld1_f16(input + 4); + vst1q_f32(output + 0, vcvt_f32_f16(in0)); + vst1q_f32(output + 4, vcvt_f32_f16(in1)); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0)); + float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4)); + vst1_f16(output+0, out0); + vst1_f16(output+4, out1); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return vgetq_lane_f32(vcvt_f32_f16(vld1_dup_f16(&h)), 0); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0).n16_u16[0]; + } + +#elif defined(STBIR_NEON) // 64-bit ARM + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + float16x8_t in = vld1q_f16(input); + vst1q_f32(output + 0, vcvt_f32_f16(vget_low_f16(in))); + vst1q_f32(output + 4, vcvt_f32_f16(vget_high_f16(in))); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0)); + float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4)); + vst1q_f16(output, vcombine_f16(out0, out1)); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return vgetq_lane_f32(vcvt_f32_f16(vdup_n_f16(h)), 0); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0); + } + +#endif + + +#ifdef STBIR_SIMD + +#define stbir__simdf_0123to3333( out, reg ) (out) = stbir__simdf_swiz( reg, 3,3,3,3 ) +#define stbir__simdf_0123to2222( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,2,2 ) +#define stbir__simdf_0123to1111( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,1,1 ) +#define stbir__simdf_0123to0000( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,0 ) +#define stbir__simdf_0123to0003( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,3 ) +#define stbir__simdf_0123to0001( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,1 ) +#define stbir__simdf_0123to1122( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,2,2 ) +#define stbir__simdf_0123to2333( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,3,3 ) +#define stbir__simdf_0123to0023( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,3 ) +#define stbir__simdf_0123to1230( out, reg ) (out) = stbir__simdf_swiz( reg, 1,2,3,0 ) +#define stbir__simdf_0123to2103( out, reg ) (out) = stbir__simdf_swiz( reg, 2,1,0,3 ) +#define stbir__simdf_0123to3210( out, reg ) (out) = stbir__simdf_swiz( reg, 3,2,1,0 ) +#define stbir__simdf_0123to2301( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,0,1 ) +#define stbir__simdf_0123to3012( out, reg ) (out) = stbir__simdf_swiz( reg, 3,0,1,2 ) +#define stbir__simdf_0123to0011( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,1,1 ) +#define stbir__simdf_0123to1100( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,0,0 ) +#define stbir__simdf_0123to2233( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,3,3 ) +#define stbir__simdf_0123to1133( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,3,3 ) +#define stbir__simdf_0123to0022( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,2 ) +#define stbir__simdf_0123to1032( out, reg ) (out) = stbir__simdf_swiz( reg, 1,0,3,2 ) + +typedef union stbir__simdi_u32 +{ + stbir_uint32 m128i_u32[4]; + int m128i_i32[4]; + stbir__simdi m128i_i128; +} stbir__simdi_u32; + +static const int STBIR_mask[9] = { 0,0,0,-1,-1,-1,0,0,0 }; + +static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float, stbir__max_uint8_as_float); +static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float, stbir__max_uint16_as_float); +static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float_inverted, stbir__max_uint8_as_float_inverted); +static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float_inverted, stbir__max_uint16_as_float_inverted); + +static const STBIR__SIMDF_CONST(STBIR_simd_point5, 0.5f); +static const STBIR__SIMDF_CONST(STBIR_ones, 1.0f); +static const STBIR__SIMDI_CONST(STBIR_almost_zero, (127 - 13) << 23); +static const STBIR__SIMDI_CONST(STBIR_almost_one, 0x3f7fffff); +static const STBIR__SIMDI_CONST(STBIR_mastissa_mask, 0xff); +static const STBIR__SIMDI_CONST(STBIR_topscale, 0x02000000); + +// Basically, in simd mode, we unroll the proper amount, and we don't want +// the non-simd remnant loops to be unroll because they only run a few times +// Adding this switch saves about 5K on clang which is Captain Unroll the 3rd. +#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star ) +#define STBIR_SIMD_NO_UNROLL(ptr) STBIR_NO_UNROLL(ptr) + +#ifdef STBIR_MEMCPY +#undef STBIR_MEMCPY +#define STBIR_MEMCPY stbir_simd_memcpy +#endif + +// override normal use of memcpy with much simpler copy (faster and smaller with our sized copies) +static void stbir_simd_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) d = (char*) dest; + char STBIR_SIMD_STREAMOUT_PTR( * ) d_end = ((char*) dest) + bytes; + ptrdiff_t ofs_to_src = (char*)src - (char*)dest; + + // check overlaps + STBIR_ASSERT( ( ( d >= ( (char*)src) + bytes ) ) || ( ( d + bytes ) <= (char*)src ) ); + + if ( bytes < (16*stbir__simdfX_float_count) ) + { + if ( bytes < 16 ) + { + if ( bytes ) + { + do + { + STBIR_SIMD_NO_UNROLL(d); + d[ 0 ] = d[ ofs_to_src ]; + ++d; + } while ( d < d_end ); + } + } + else + { + stbir__simdf x; + // do one unaligned to get us aligned for the stream out below + stbir__simdf_load( x, ( d + ofs_to_src ) ); + stbir__simdf_store( d, x ); + d = (char*)( ( ( (ptrdiff_t)d ) + 16 ) & ~15 ); + + for(;;) + { + STBIR_SIMD_NO_UNROLL(d); + + if ( d > ( d_end - 16 ) ) + { + if ( d == d_end ) + return; + d = d_end - 16; + } + + stbir__simdf_load( x, ( d + ofs_to_src ) ); + stbir__simdf_store( d, x ); + d += 16; + } + } + } + else + { + stbir__simdfX x0,x1,x2,x3; + + // do one unaligned to get us aligned for the stream out below + stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count ); + stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count ); + stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count ); + stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count ); + stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 ); + stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 ); + stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 ); + stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 ); + d = (char*)( ( ( (ptrdiff_t)d ) + (16*stbir__simdfX_float_count) ) & ~((16*stbir__simdfX_float_count)-1) ); + + for(;;) + { + STBIR_SIMD_NO_UNROLL(d); + + if ( d > ( d_end - (16*stbir__simdfX_float_count) ) ) + { + if ( d == d_end ) + return; + d = d_end - (16*stbir__simdfX_float_count); + } + + stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count ); + stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count ); + stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count ); + stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count ); + stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 ); + stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 ); + stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 ); + stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 ); + d += (16*stbir__simdfX_float_count); + } + } +} + +// memcpy that is specically intentionally overlapping (src is smaller then dest, so can be +// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to +// the diff between dest and src) +static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src; + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes; + ptrdiff_t ofs_to_dest = (char*)dest - (char*)src; + + if ( ofs_to_dest >= 16 ) // is the overlap more than 16 away? + { + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end16 = ((char*) src) + (bytes&~15); + do + { + stbir__simdf x; + STBIR_SIMD_NO_UNROLL(sd); + stbir__simdf_load( x, sd ); + stbir__simdf_store( ( sd + ofs_to_dest ), x ); + sd += 16; + } while ( sd < s_end16 ); + + if ( sd == s_end ) + return; + } + + do + { + STBIR_SIMD_NO_UNROLL(sd); + *(int*)( sd + ofs_to_dest ) = *(int*) sd; + sd += 4; + } while ( sd < s_end ); +} + +#else // no SSE2 + +// when in scalar mode, we let unrolling happen, so this macro just does the __restrict +#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star ) +#define STBIR_SIMD_NO_UNROLL(ptr) + +#endif // SSE2 + + +#ifdef STBIR_PROFILE + +#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(__SSE2__) || defined(STBIR_SSE) || defined( _M_IX86_FP ) || defined(__i386) || defined( __i386__ ) || defined( _M_IX86 ) || defined( _X86_ ) + +#ifdef _MSC_VER + + STBIRDEF stbir_uint64 __rdtsc(); + #define STBIR_PROFILE_FUNC() __rdtsc() + +#else // non msvc + + static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC() + { + stbir_uint32 lo, hi; + asm volatile ("rdtsc" : "=a" (lo), "=d" (hi) ); + return ( ( (stbir_uint64) hi ) << 32 ) | ( (stbir_uint64) lo ); + } + +#endif // msvc + +#elif defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || defined(__ARM_NEON__) + +#if defined( _MSC_VER ) && !defined(__clang__) + + #define STBIR_PROFILE_FUNC() _ReadStatusReg(ARM64_CNTVCT) + +#else + + static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC() + { + stbir_uint64 tsc; + asm volatile("mrs %0, cntvct_el0" : "=r" (tsc)); + return tsc; + } + +#endif + +#else // x64, arm + +#error Unknown platform for profiling. + +#endif //x64 and + + +#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO ,stbir__per_split_info * split_info +#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO ,split_info + +#define STBIR_ONLY_PROFILE_BUILD_GET_INFO ,stbir__info * profile_info +#define STBIR_ONLY_PROFILE_BUILD_SET_INFO ,profile_info + +// super light-weight micro profiler +#define STBIR_PROFILE_START_ll( info, wh ) { stbir_uint64 wh##thiszonetime = STBIR_PROFILE_FUNC(); stbir_uint64 * wh##save_parent_excluded_ptr = info->current_zone_excluded_ptr; stbir_uint64 wh##current_zone_excluded = 0; info->current_zone_excluded_ptr = &wh##current_zone_excluded; +#define STBIR_PROFILE_END_ll( info, wh ) wh##thiszonetime = STBIR_PROFILE_FUNC() - wh##thiszonetime; info->profile.named.wh += wh##thiszonetime - wh##current_zone_excluded; *wh##save_parent_excluded_ptr += wh##thiszonetime; info->current_zone_excluded_ptr = wh##save_parent_excluded_ptr; } +#define STBIR_PROFILE_FIRST_START_ll( info, wh ) { int i; info->current_zone_excluded_ptr = &info->profile.named.total; for(i=0;iprofile.array);i++) info->profile.array[i]=0; } STBIR_PROFILE_START_ll( info, wh ); +#define STBIR_PROFILE_CLEAR_EXTRAS_ll( info, num ) { int extra; for(extra=1;extra<(num);extra++) { int i; for(i=0;iprofile.array);i++) (info)[extra].profile.array[i]=0; } } + +// for thread data +#define STBIR_PROFILE_START( wh ) STBIR_PROFILE_START_ll( split_info, wh ) +#define STBIR_PROFILE_END( wh ) STBIR_PROFILE_END_ll( split_info, wh ) +#define STBIR_PROFILE_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( split_info, wh ) +#define STBIR_PROFILE_CLEAR_EXTRAS() STBIR_PROFILE_CLEAR_EXTRAS_ll( split_info, split_count ) + +// for build data +#define STBIR_PROFILE_BUILD_START( wh ) STBIR_PROFILE_START_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_END( wh ) STBIR_PROFILE_END_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_CLEAR( info ) { int i; for(i=0;iprofile.array);i++) info->profile.array[i]=0; } + +#else // no profile + +#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO +#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO + +#define STBIR_ONLY_PROFILE_BUILD_GET_INFO +#define STBIR_ONLY_PROFILE_BUILD_SET_INFO + +#define STBIR_PROFILE_START( wh ) +#define STBIR_PROFILE_END( wh ) +#define STBIR_PROFILE_FIRST_START( wh ) +#define STBIR_PROFILE_CLEAR_EXTRAS( ) + +#define STBIR_PROFILE_BUILD_START( wh ) +#define STBIR_PROFILE_BUILD_END( wh ) +#define STBIR_PROFILE_BUILD_FIRST_START( wh ) +#define STBIR_PROFILE_BUILD_CLEAR( info ) + +#endif // stbir_profile + +#ifndef STBIR_CEILF +#include +#if _MSC_VER <= 1200 // support VC6 for Sean +#define STBIR_CEILF(x) ((float)ceil((float)(x))) +#define STBIR_FLOORF(x) ((float)floor((float)(x))) +#else +#define STBIR_CEILF(x) ceilf(x) +#define STBIR_FLOORF(x) floorf(x) +#endif +#endif + +#ifndef STBIR_MEMCPY +// For memcpy +#include +#define STBIR_MEMCPY( dest, src, len ) memcpy( dest, src, len ) +#endif + +#ifndef STBIR_SIMD + +// memcpy that is specically intentionally overlapping (src is smaller then dest, so can be +// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to +// the diff between dest and src) +static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src; + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes; + ptrdiff_t ofs_to_dest = (char*)dest - (char*)src; + + if ( ofs_to_dest >= 8 ) // is the overlap more than 8 away? + { + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end8 = ((char*) src) + (bytes&~7); + do + { + STBIR_NO_UNROLL(sd); + *(stbir_uint64*)( sd + ofs_to_dest ) = *(stbir_uint64*) sd; + sd += 8; + } while ( sd < s_end8 ); + + if ( sd == s_end ) + return; + } + + do + { + STBIR_NO_UNROLL(sd); + *(int*)( sd + ofs_to_dest ) = *(int*) sd; + sd += 4; + } while ( sd < s_end ); +} + +#endif + +static float stbir__filter_trapezoid(float x, float scale, void * user_data) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR_ASSERT(scale <= 1); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x >= t) + return 0.0f; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1.0f; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale, void * user_data) +{ + STBIR__UNUSED(user_data); + return 0.5f + scale / 2.0f; +} + +static float stbir__filter_triangle(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x <= 1.0f) + return 1.0f - x; + else + return 0.0f; +} + +static float stbir__filter_point(float x, float s, void * user_data) +{ + STBIR__UNUSED(x); + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + return 1.0f; +} + +static float stbir__filter_cubic(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return (4.0f + x*x*(3.0f*x - 6.0f))/6.0f; + else if (x < 2.0f) + return (8.0f + x*(-12.0f + x*(6.0f - x)))/6.0f; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return 1.0f - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2.0f - x*(4.0f + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return (16.0f + x*x*(21.0f * x - 36.0f))/18.0f; + else if (x < 2.0f) + return (32.0f + x*(-60.0f + x*(36.0f - 7.0f*x)))/18.0f; + + return (0.0f); +} + +static float stbir__support_zero(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 0; +} + +static float stbir__support_zeropoint5(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 0.5f; +} + +static float stbir__support_one(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 1; +} + +static float stbir__support_two(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 2; +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter from the output pixel's perspective +static int stbir__get_filter_pixel_width(stbir__support_callback * support, float scale, void * user_data) +{ + STBIR_ASSERT(support != 0); + + if ( scale >= ( 1.0f-stbir__small_float ) ) // upscale + return (int)STBIR_CEILF(support(1.0f/scale,user_data) * 2.0f); + else + return (int)STBIR_CEILF(support(scale,user_data) * 2.0f / scale); +} + +// this is how many coefficents per run of the filter (which is different +// from the filter_pixel_width depending on if we are scattering or gathering) +static int stbir__get_coefficient_width(stbir__sampler * samp, int is_gather, void * user_data) +{ + float scale = samp->scale_info.scale; + stbir__support_callback * support = samp->filter_support; + + switch( is_gather ) + { + case 1: + return (int)STBIR_CEILF(support(1.0f / scale, user_data) * 2.0f); + case 2: + return (int)STBIR_CEILF(support(scale, user_data) * 2.0f / scale); + case 0: + return (int)STBIR_CEILF(support(scale, user_data) * 2.0f); + default: + STBIR_ASSERT( (is_gather >= 0 ) && (is_gather <= 2 ) ); + return 0; + } +} + +static int stbir__get_contributors(stbir__sampler * samp, int is_gather) +{ + if (is_gather) + return samp->scale_info.output_sub_size; + else + return (samp->scale_info.input_full_size + samp->filter_pixel_margin * 2); +} + +static int stbir__edge_zero_full( int n, int max ) +{ + STBIR__UNUSED(n); + STBIR__UNUSED(max); + return 0; // NOTREACHED +} + +static int stbir__edge_clamp_full( int n, int max ) +{ + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED +} + +static int stbir__edge_reflect_full( int n, int max ) +{ + if (n < 0) + { + if (n > -max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED +} + +static int stbir__edge_wrap_full( int n, int max ) +{ + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } +} + +typedef int stbir__edge_wrap_func( int n, int max ); +static stbir__edge_wrap_func * stbir__edge_wrap_slow[] = +{ + stbir__edge_clamp_full, // STBIR_EDGE_CLAMP + stbir__edge_reflect_full, // STBIR_EDGE_REFLECT + stbir__edge_wrap_full, // STBIR_EDGE_WRAP + stbir__edge_zero_full, // STBIR_EDGE_ZERO +}; + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow[edge]( n, max ); +} + +#define STBIR__MERGE_RUNS_PIXEL_THRESHOLD 16 + +// get information on the extents of a sampler +static void stbir__get_extents( stbir__sampler * samp, stbir__extents * scanline_extents ) +{ + int j, stop; + int left_margin, right_margin; + int min_n = 0x7fffffff, max_n = -0x7fffffff; + int min_left = 0x7fffffff, max_left = -0x7fffffff; + int min_right = 0x7fffffff, max_right = -0x7fffffff; + stbir_edge edge = samp->edge; + stbir__contributors* contributors = samp->contributors; + int output_sub_size = samp->scale_info.output_sub_size; + int input_full_size = samp->scale_info.input_full_size; + int filter_pixel_margin = samp->filter_pixel_margin; + + STBIR_ASSERT( samp->is_gather ); + + stop = output_sub_size; + for (j = 0; j < stop; j++ ) + { + STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 ); + if ( contributors[j].n0 < min_n ) + { + min_n = contributors[j].n0; + stop = j + filter_pixel_margin; // if we find a new min, only scan another filter width + if ( stop > output_sub_size ) stop = output_sub_size; + } + } + + stop = 0; + for (j = output_sub_size - 1; j >= stop; j-- ) + { + STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 ); + if ( contributors[j].n1 > max_n ) + { + max_n = contributors[j].n1; + stop = j - filter_pixel_margin; // if we find a new max, only scan another filter width + if (stop<0) stop = 0; + } + } + + STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n ); + STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n ); + + // now calculate how much into the margins we really read + left_margin = 0; + if ( min_n < 0 ) + { + left_margin = -min_n; + min_n = 0; + } + + right_margin = 0; + if ( max_n >= input_full_size ) + { + right_margin = max_n - input_full_size + 1; + max_n = input_full_size - 1; + } + + // index 1 is margin pixel extents (how many pixels we hang over the edge) + scanline_extents->edge_sizes[0] = left_margin; + scanline_extents->edge_sizes[1] = right_margin; + + // index 2 is pixels read from the input + scanline_extents->spans[0].n0 = min_n; + scanline_extents->spans[0].n1 = max_n; + scanline_extents->spans[0].pixel_offset_for_input = min_n; + + // default to no other input range + scanline_extents->spans[1].n0 = 0; + scanline_extents->spans[1].n1 = -1; + scanline_extents->spans[1].pixel_offset_for_input = 0; + + // don't have to do edge calc for zero clamp + if ( edge == STBIR_EDGE_ZERO ) + return; + + // convert margin pixels to the pixels within the input (min and max) + for( j = -left_margin ; j < 0 ; j++ ) + { + int p = stbir__edge_wrap( edge, j, input_full_size ); + if ( p < min_left ) + min_left = p; + if ( p > max_left ) + max_left = p; + } + + for( j = input_full_size ; j < (input_full_size + right_margin) ; j++ ) + { + int p = stbir__edge_wrap( edge, j, input_full_size ); + if ( p < min_right ) + min_right = p; + if ( p > max_right ) + max_right = p; + } + + // merge the left margin pixel region if it connects within 4 pixels of main pixel region + if ( min_left != 0x7fffffff ) + { + if ( ( ( min_left <= min_n ) && ( ( max_left + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) || + ( ( min_n <= min_left ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_left ) ) ) + { + scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_left ); + scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_left ); + scanline_extents->spans[0].pixel_offset_for_input = min_n; + left_margin = 0; + } + } + + // merge the right margin pixel region if it connects within 4 pixels of main pixel region + if ( min_right != 0x7fffffff ) + { + if ( ( ( min_right <= min_n ) && ( ( max_right + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) || + ( ( min_n <= min_right ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_right ) ) ) + { + scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_right ); + scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_right ); + scanline_extents->spans[0].pixel_offset_for_input = min_n; + right_margin = 0; + } + } + + STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n ); + STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n ); + + // you get two ranges when you have the WRAP edge mode and you are doing just the a piece of the resize + // so you need to get a second run of pixels from the opposite side of the scanline (which you + // wouldn't need except for WRAP) + + + // if we can't merge the min_left range, add it as a second range + if ( ( left_margin ) && ( min_left != 0x7fffffff ) ) + { + stbir__span * newspan = scanline_extents->spans + 1; + STBIR_ASSERT( right_margin == 0 ); + if ( min_left < scanline_extents->spans[0].n0 ) + { + scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1; + --newspan; + } + newspan->pixel_offset_for_input = min_left; + newspan->n0 = -left_margin; + newspan->n1 = ( max_left - min_left ) - left_margin; + scanline_extents->edge_sizes[0] = 0; // don't need to copy the left margin, since we are directly decoding into the margin + return; + } + + // if we can't merge the min_left range, add it as a second range + if ( ( right_margin ) && ( min_right != 0x7fffffff ) ) + { + stbir__span * newspan = scanline_extents->spans + 1; + if ( min_right < scanline_extents->spans[0].n0 ) + { + scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1; + --newspan; + } + newspan->pixel_offset_for_input = min_right; + newspan->n0 = scanline_extents->spans[1].n1 + 1; + newspan->n1 = scanline_extents->spans[1].n1 + 1 + ( max_right - min_right ); + scanline_extents->edge_sizes[1] = 0; // don't need to copy the right margin, since we are directly decoding into the margin + return; + } +} + +static void stbir__calculate_in_pixel_range( int * first_pixel, int * last_pixel, float out_pixel_center, float out_filter_radius, float inv_scale, float out_shift, int input_size, stbir_edge edge ) +{ + int first, last; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) * inv_scale; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) * inv_scale; + + first = (int)(STBIR_FLOORF(in_pixel_influence_lowerbound + 0.5f)); + last = (int)(STBIR_FLOORF(in_pixel_influence_upperbound - 0.5f)); + + if ( edge == STBIR_EDGE_WRAP ) + { + if ( first <= -input_size ) + first = -(input_size-1); + if ( last >= (input_size*2)) + last = (input_size*2) - 1; + } + + *first_pixel = first; + *last_pixel = last; +} + +static void stbir__calculate_coefficients_for_gather_upsample( float out_filter_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float* coefficient_group, int coefficient_width, stbir_edge edge, void * user_data ) +{ + int n, end; + float inv_scale = scale_info->inv_scale; + float out_shift = scale_info->pixel_shift; + int input_size = scale_info->input_full_size; + int numerator = scale_info->scale_numerator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) ); + + // Looping through out pixels + end = num_contributors; if ( polyphase ) end = numerator; + for (n = 0; n < end; n++) + { + int i; + int last_non_zero; + float out_pixel_center = (float)n + 0.5f; + float in_center_of_out = (out_pixel_center + out_shift) * inv_scale; + + int in_first_pixel, in_last_pixel; + + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, out_pixel_center, out_filter_radius, inv_scale, out_shift, input_size, edge ); + + last_non_zero = -1; + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + float coeff = kernel(in_center_of_out - in_pixel_center, inv_scale, user_data); + + // kill denormals + if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) ) + { + if ( i == 0 ) // if we're at the front, just eat zero contributors + { + STBIR_ASSERT ( ( in_last_pixel - in_first_pixel ) != 0 ); // there should be at least one contrib + ++in_first_pixel; + i--; + continue; + } + coeff = 0; // make sure is fully zero (should keep denormals away) + } + else + last_non_zero = i; + + coefficient_group[i] = coeff; + } + + in_last_pixel = last_non_zero+in_first_pixel; // kills trailing zeros + contributors->n0 = in_first_pixel; + contributors->n1 = in_last_pixel; + + STBIR_ASSERT(contributors->n1 >= contributors->n0); + + ++contributors; + coefficient_group += coefficient_width; + } +} + +static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs, int new_pixel, float new_coeff ) +{ + if ( new_pixel <= contribs->n1 ) // before the end + { + if ( new_pixel < contribs->n0 ) // before the front? + { + int j, o = contribs->n0 - new_pixel; + for ( j = contribs->n1 - contribs->n0 ; j <= 0 ; j-- ) + coeffs[ j + o ] = coeffs[ j ]; + for ( j = 1 ; j < o ; j-- ) + coeffs[ j ] = coeffs[ 0 ]; + coeffs[ 0 ] = new_coeff; + contribs->n0 = new_pixel; + } + else + { + coeffs[ new_pixel - contribs->n0 ] += new_coeff; + } + } + else + { + int j, e = new_pixel - contribs->n0; + for( j = ( contribs->n1 - contribs->n0 ) + 1 ; j < e ; j++ ) // clear in-betweens coeffs if there are any + coeffs[j] = 0; + + coeffs[ e ] = new_coeff; + contribs->n1 = new_pixel; + } +} + +static void stbir__calculate_out_pixel_range( int * first_pixel, int * last_pixel, float in_pixel_center, float in_pixels_radius, float scale, float out_shift, int out_size ) +{ + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale - out_shift; + int out_first_pixel = (int)(STBIR_FLOORF(out_pixel_influence_lowerbound + 0.5f)); + int out_last_pixel = (int)(STBIR_FLOORF(out_pixel_influence_upperbound - 0.5f)); + + if ( out_first_pixel < 0 ) + out_first_pixel = 0; + if ( out_last_pixel >= out_size ) + out_last_pixel = out_size - 1; + *first_pixel = out_first_pixel; + *last_pixel = out_last_pixel; +} + +static void stbir__calculate_coefficients_for_gather_downsample( int start, int end, float in_pixels_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int coefficient_width, int num_contributors, stbir__contributors * contributors, float * coefficient_group, void * user_data ) +{ + int in_pixel; + int i; + int first_out_inited = -1; + float scale = scale_info->scale; + float out_shift = scale_info->pixel_shift; + int out_size = scale_info->output_sub_size; + int numerator = scale_info->scale_numerator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < out_size ) ); + + STBIR__UNUSED(num_contributors); + + // Loop through the input pixels + for (in_pixel = start; in_pixel < end; in_pixel++) + { + float in_pixel_center = (float)in_pixel + 0.5f; + float out_center_of_in = in_pixel_center * scale - out_shift; + int out_first_pixel, out_last_pixel; + + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, in_pixel_center, in_pixels_radius, scale, out_shift, out_size ); + + if ( out_first_pixel > out_last_pixel ) + continue; + + // clamp or exit if we are using polyphase filtering, and the limit is up + if ( polyphase ) + { + // when polyphase, you only have to do coeffs up to the numerator count + if ( out_first_pixel == numerator ) + break; + + // don't do any extra work, clamp last pixel at numerator too + if ( out_last_pixel >= numerator ) + out_last_pixel = numerator - 1; + } + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + float coeff = kernel(x, scale, user_data) * scale; + + // kill the coeff if it's too small (avoid denormals) + if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) ) + coeff = 0.0f; + + { + int out = i + out_first_pixel; + float * coeffs = coefficient_group + out * coefficient_width; + stbir__contributors * contribs = contributors + out; + + // is this the first time this output pixel has been seen? Init it. + if ( out > first_out_inited ) + { + STBIR_ASSERT( out == ( first_out_inited + 1 ) ); // ensure we have only advanced one at time + first_out_inited = out; + contribs->n0 = in_pixel; + contribs->n1 = in_pixel; + coeffs[0] = coeff; + } + else + { + // insert on end (always in order) + if ( coeffs[0] == 0.0f ) // if the first coefficent is zero, then zap it for this coeffs + { + STBIR_ASSERT( ( in_pixel - contribs->n0 ) == 1 ); // ensure that when we zap, we're at the 2nd pos + contribs->n0 = in_pixel; + } + contribs->n1 = in_pixel; + STBIR_ASSERT( ( in_pixel - contribs->n0 ) < coefficient_width ); + coeffs[in_pixel - contribs->n0] = coeff; + } + } + } + } +} + +static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter_extent_info* filter_info, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float * coefficient_group, int coefficient_width ) +{ + int input_size = scale_info->input_full_size; + int input_last_n1 = input_size - 1; + int n, end; + int lowest = 0x7fffffff; + int highest = -0x7fffffff; + int widest = -1; + int numerator = scale_info->scale_numerator; + int denominator = scale_info->scale_denominator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) ); + float * coeffs; + stbir__contributors * contribs; + + // weight all the coeffs for each sample + coeffs = coefficient_group; + contribs = contributors; + end = num_contributors; if ( polyphase ) end = numerator; + for (n = 0; n < end; n++) + { + int i; + float filter_scale, total_filter = 0; + int e; + + // add all contribs + e = contribs->n1 - contribs->n0; + for( i = 0 ; i <= e ; i++ ) + { + total_filter += coeffs[i]; + STBIR_ASSERT( ( coeffs[i] >= -2.0f ) && ( coeffs[i] <= 2.0f ) ); // check for wonky weights + } + + // rescale + if ( ( total_filter < stbir__small_float ) && ( total_filter > -stbir__small_float ) ) + { + // all coeffs are extremely small, just zero it + contribs->n1 = contribs->n0; + coeffs[0] = 0.0f; + } + else + { + // if the total isn't 1.0, rescale everything + if ( ( total_filter < (1.0f-stbir__small_float) ) || ( total_filter > (1.0f+stbir__small_float) ) ) + { + filter_scale = 1.0f / total_filter; + // scale them all + for (i = 0; i <= e; i++) + coeffs[i] *= filter_scale; + } + } + ++contribs; + coeffs += coefficient_width; + } + + // if we have a rational for the scale, we can exploit the polyphaseness to not calculate + // most of the coefficients, so we copy them here + if ( polyphase ) + { + stbir__contributors * prev_contribs = contributors; + stbir__contributors * cur_contribs = contributors + numerator; + + for( n = numerator ; n < num_contributors ; n++ ) + { + cur_contribs->n0 = prev_contribs->n0 + denominator; + cur_contribs->n1 = prev_contribs->n1 + denominator; + ++cur_contribs; + ++prev_contribs; + } + stbir_overlapping_memcpy( coefficient_group + numerator * coefficient_width, coefficient_group, ( num_contributors - numerator ) * coefficient_width * sizeof( coeffs[ 0 ] ) ); + } + + coeffs = coefficient_group; + contribs = contributors; + for (n = 0; n < num_contributors; n++) + { + int i; + + // in zero edge mode, just remove out of bounds contribs completely (since their weights are accounted for now) + if ( edge == STBIR_EDGE_ZERO ) + { + // shrink the right side if necessary + if ( contribs->n1 > input_last_n1 ) + contribs->n1 = input_last_n1; + + // shrink the left side + if ( contribs->n0 < 0 ) + { + int j, left, skips = 0; + + skips = -contribs->n0; + contribs->n0 = 0; + + // now move down the weights + left = contribs->n1 - contribs->n0 + 1; + if ( left > 0 ) + { + for( j = 0 ; j < left ; j++ ) + coeffs[ j ] = coeffs[ j + skips ]; + } + } + } + else if ( ( edge == STBIR_EDGE_CLAMP ) || ( edge == STBIR_EDGE_REFLECT ) ) + { + // for clamp and reflect, calculate the true inbounds position (based on edge type) and just add that to the existing weight + + // right hand side first + if ( contribs->n1 > input_last_n1 ) + { + int start = contribs->n0; + int endi = contribs->n1; + contribs->n1 = input_last_n1; + for( i = input_size; i <= endi; i++ ) + stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), coeffs[i-start] ); + } + + // now check left hand edge + if ( contribs->n0 < 0 ) + { + int save_n0; + float save_n0_coeff; + float * c = coeffs - ( contribs->n0 + 1 ); + + // reinsert the coeffs with it reflected or clamped (insert accumulates, if the coeffs exist) + for( i = -1 ; i > contribs->n0 ; i-- ) + stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), *c-- ); + save_n0 = contribs->n0; + save_n0_coeff = c[0]; // save it, since we didn't do the final one (i==n0), because there might be too many coeffs to hold (before we resize)! + + // now slide all the coeffs down (since we have accumulated them in the positive contribs) and reset the first contrib + contribs->n0 = 0; + for(i = 0 ; i <= contribs->n1 ; i++ ) + coeffs[i] = coeffs[i-save_n0]; + + // now that we have shrunk down the contribs, we insert the first one safely + stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( save_n0, input_size ), save_n0_coeff ); + } + } + + if ( contribs->n0 <= contribs->n1 ) + { + int diff = contribs->n1 - contribs->n0 + 1; + while ( diff && ( coeffs[ diff-1 ] == 0.0f ) ) + --diff; + contribs->n1 = contribs->n0 + diff - 1; + + if ( contribs->n0 <= contribs->n1 ) + { + if ( contribs->n0 < lowest ) + lowest = contribs->n0; + if ( contribs->n1 > highest ) + highest = contribs->n1; + if ( diff > widest ) + widest = diff; + } + + // re-zero out unused coefficients (if any) + for( i = diff ; i < coefficient_width ; i++ ) + coeffs[i] = 0.0f; + } + + ++contribs; + coeffs += coefficient_width; + } + filter_info->lowest = lowest; + filter_info->highest = highest; + filter_info->widest = widest; +} + +static int stbir__pack_coefficients( int num_contributors, stbir__contributors* contributors, float * coefficents, int coefficient_width, int widest, int row_width ) +{ + #define STBIR_MOVE_1( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint32*)(dest))[0] = ((stbir_uint32*)(src))[0]; } + #define STBIR_MOVE_2( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; } + #ifdef STBIR_SIMD + #define STBIR_MOVE_4( dest, src ) { stbir__simdf t; STBIR_NO_UNROLL(dest); stbir__simdf_load( t, src ); stbir__simdf_store( dest, t ); } + #else + #define STBIR_MOVE_4( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; ((stbir_uint64*)(dest))[1] = ((stbir_uint64*)(src))[1]; } + #endif + if ( coefficient_width != widest ) + { + float * pc = coefficents; + float * coeffs = coefficents; + float * pc_end = coefficents + num_contributors * widest; + switch( widest ) + { + case 1: + do { + STBIR_MOVE_1( pc, coeffs ); + ++pc; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 2: + do { + STBIR_MOVE_2( pc, coeffs ); + pc += 2; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 3: + do { + STBIR_MOVE_2( pc, coeffs ); + STBIR_MOVE_1( pc+2, coeffs+2 ); + pc += 3; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 4: + do { + STBIR_MOVE_4( pc, coeffs ); + pc += 4; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 5: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_1( pc+4, coeffs+4 ); + pc += 5; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 6: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_2( pc+4, coeffs+4 ); + pc += 6; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 7: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_2( pc+4, coeffs+4 ); + STBIR_MOVE_1( pc+6, coeffs+6 ); + pc += 7; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 8: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + pc += 8; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 9: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_1( pc+8, coeffs+8 ); + pc += 9; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 10: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_2( pc+8, coeffs+8 ); + pc += 10; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 11: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_2( pc+8, coeffs+8 ); + STBIR_MOVE_1( pc+10, coeffs+10 ); + pc += 11; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 12: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_4( pc+8, coeffs+8 ); + pc += 12; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + default: + do { + float * copy_end = pc + widest - 4; + float * c = coeffs; + do { + STBIR_NO_UNROLL( pc ); + STBIR_MOVE_4( pc, c ); + pc += 4; + c += 4; + } while ( pc <= copy_end ); + copy_end += 4; + while ( pc < copy_end ) + { + STBIR_MOVE_1( pc, c ); + ++pc; ++c; + } + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + } + } + + // some horizontal routines read one float off the end (which is then masked off), so put in a sentinal so we don't read an snan or denormal + coefficents[ widest * num_contributors ] = 8888.0f; + + // the minimum we might read for unrolled filters widths is 12. So, we need to + // make sure we never read outside the decode buffer, by possibly moving + // the sample area back into the scanline, and putting zeros weights first. + // we start on the right edge and check until we're well past the possible + // clip area (2*widest). + { + stbir__contributors * contribs = contributors + num_contributors - 1; + float * coeffs = coefficents + widest * ( num_contributors - 1 ); + + // go until no chance of clipping (this is usually less than 8 lops) + while ( ( ( contribs->n0 + widest*2 ) >= row_width ) && ( contribs >= contributors ) ) + { + // might we clip?? + if ( ( contribs->n0 + widest ) > row_width ) + { + int stop_range = widest; + + // if range is larger than 12, it will be handled by generic loops that can terminate on the exact length + // of this contrib n1, instead of a fixed widest amount - so calculate this + if ( widest > 12 ) + { + int mod; + + // how far will be read in the n_coeff loop (which depends on the widest count mod4); + mod = widest & 3; + stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod; + + // the n_coeff loops do a minimum amount of coeffs, so factor that in! + if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod; + } + + // now see if we still clip with the refined range + if ( ( contribs->n0 + stop_range ) > row_width ) + { + int new_n0 = row_width - stop_range; + int num = contribs->n1 - contribs->n0 + 1; + int backup = contribs->n0 - new_n0; + float * from_co = coeffs + num - 1; + float * to_co = from_co + backup; + + STBIR_ASSERT( ( new_n0 >= 0 ) && ( new_n0 < contribs->n0 ) ); + + // move the coeffs over + while( num ) + { + *to_co-- = *from_co--; + --num; + } + // zero new positions + while ( to_co >= coeffs ) + *to_co-- = 0; + // set new start point + contribs->n0 = new_n0; + if ( widest > 12 ) + { + int mod; + + // how far will be read in the n_coeff loop (which depends on the widest count mod4); + mod = widest & 3; + stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod; + + // the n_coeff loops do a minimum amount of coeffs, so factor that in! + if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod; + } + } + } + --contribs; + coeffs -= widest; + } + } + + return widest; + #undef STBIR_MOVE_1 + #undef STBIR_MOVE_2 + #undef STBIR_MOVE_4 +} + +static void stbir__calculate_filters( stbir__sampler * samp, stbir__sampler * other_axis_for_pivot, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO ) +{ + int n; + float scale = samp->scale_info.scale; + stbir__kernel_callback * kernel = samp->filter_kernel; + stbir__support_callback * support = samp->filter_support; + float inv_scale = samp->scale_info.inv_scale; + int input_full_size = samp->scale_info.input_full_size; + int gather_num_contributors = samp->num_contributors; + stbir__contributors* gather_contributors = samp->contributors; + float * gather_coeffs = samp->coefficients; + int gather_coefficient_width = samp->coefficient_width; + + switch ( samp->is_gather ) + { + case 1: // gather upsample + { + float out_pixels_radius = support(inv_scale,user_data) * scale; + + stbir__calculate_coefficients_for_gather_upsample( out_pixels_radius, kernel, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width, samp->edge, user_data ); + + STBIR_PROFILE_BUILD_START( cleanup ); + stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width ); + STBIR_PROFILE_BUILD_END( cleanup ); + } + break; + + case 0: // scatter downsample (only on vertical) + case 2: // gather downsample + { + float in_pixels_radius = support(scale,user_data) * inv_scale; + int filter_pixel_margin = samp->filter_pixel_margin; + int input_end = input_full_size + filter_pixel_margin; + + // if this is a scatter, we do a downsample gather to get the coeffs, and then pivot after + if ( !samp->is_gather ) + { + // check if we are using the same gather downsample on the horizontal as this vertical, + // if so, then we don't have to generate them, we can just pivot from the horizontal. + if ( other_axis_for_pivot ) + { + gather_contributors = other_axis_for_pivot->contributors; + gather_coeffs = other_axis_for_pivot->coefficients; + gather_coefficient_width = other_axis_for_pivot->coefficient_width; + gather_num_contributors = other_axis_for_pivot->num_contributors; + samp->extent_info.lowest = other_axis_for_pivot->extent_info.lowest; + samp->extent_info.highest = other_axis_for_pivot->extent_info.highest; + samp->extent_info.widest = other_axis_for_pivot->extent_info.widest; + goto jump_right_to_pivot; + } + + gather_contributors = samp->gather_prescatter_contributors; + gather_coeffs = samp->gather_prescatter_coefficients; + gather_coefficient_width = samp->gather_prescatter_coefficient_width; + gather_num_contributors = samp->gather_prescatter_num_contributors; + } + + stbir__calculate_coefficients_for_gather_downsample( -filter_pixel_margin, input_end, in_pixels_radius, kernel, &samp->scale_info, gather_coefficient_width, gather_num_contributors, gather_contributors, gather_coeffs, user_data ); + + STBIR_PROFILE_BUILD_START( cleanup ); + stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width ); + STBIR_PROFILE_BUILD_END( cleanup ); + + if ( !samp->is_gather ) + { + // if this is a scatter (vertical only), then we need to pivot the coeffs + stbir__contributors * scatter_contributors; + int highest_set; + + jump_right_to_pivot: + + STBIR_PROFILE_BUILD_START( pivot ); + + highest_set = (-filter_pixel_margin) - 1; + for (n = 0; n < gather_num_contributors; n++) + { + int k; + int gn0 = gather_contributors->n0, gn1 = gather_contributors->n1; + int scatter_coefficient_width = samp->coefficient_width; + float * scatter_coeffs = samp->coefficients + ( gn0 + filter_pixel_margin ) * scatter_coefficient_width; + float * g_coeffs = gather_coeffs; + scatter_contributors = samp->contributors + ( gn0 + filter_pixel_margin ); + + for (k = gn0 ; k <= gn1 ; k++ ) + { + float gc = *g_coeffs++; + if ( ( k > highest_set ) || ( scatter_contributors->n0 > scatter_contributors->n1 ) ) + { + { + // if we are skipping over several contributors, we need to clear the skipped ones + stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1); + while ( clear_contributors < scatter_contributors ) + { + clear_contributors->n0 = 0; + clear_contributors->n1 = -1; + ++clear_contributors; + } + } + scatter_contributors->n0 = n; + scatter_contributors->n1 = n; + scatter_coeffs[0] = gc; + highest_set = k; + } + else + { + stbir__insert_coeff( scatter_contributors, scatter_coeffs, n, gc ); + } + ++scatter_contributors; + scatter_coeffs += scatter_coefficient_width; + } + + ++gather_contributors; + gather_coeffs += gather_coefficient_width; + } + + // now clear any unset contribs + { + stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1); + stbir__contributors * end_contributors = samp->contributors + samp->num_contributors; + while ( clear_contributors < end_contributors ) + { + clear_contributors->n0 = 0; + clear_contributors->n1 = -1; + ++clear_contributors; + } + } + + STBIR_PROFILE_BUILD_END( pivot ); + } + } + break; + } +} + + +//======================================================================================================== +// scanline decoders and encoders + +#define stbir__coder_min_num 1 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix BGRA +#define stbir__decode_swizzle +#define stbir__decode_order0 2 +#define stbir__decode_order1 1 +#define stbir__decode_order2 0 +#define stbir__decode_order3 3 +#define stbir__encode_order0 2 +#define stbir__encode_order1 1 +#define stbir__encode_order2 0 +#define stbir__encode_order3 3 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix ARGB +#define stbir__decode_swizzle +#define stbir__decode_order0 1 +#define stbir__decode_order1 2 +#define stbir__decode_order2 3 +#define stbir__decode_order3 0 +#define stbir__encode_order0 3 +#define stbir__encode_order1 0 +#define stbir__encode_order2 1 +#define stbir__encode_order3 2 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix ABGR +#define stbir__decode_swizzle +#define stbir__decode_order0 3 +#define stbir__decode_order1 2 +#define stbir__decode_order2 1 +#define stbir__decode_order3 0 +#define stbir__encode_order0 3 +#define stbir__encode_order1 2 +#define stbir__encode_order2 1 +#define stbir__encode_order3 0 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix AR +#define stbir__decode_swizzle +#define stbir__decode_order0 1 +#define stbir__decode_order1 0 +#define stbir__decode_order2 3 +#define stbir__decode_order3 2 +#define stbir__encode_order0 1 +#define stbir__encode_order1 0 +#define stbir__encode_order2 3 +#define stbir__encode_order3 2 +#define stbir__coder_min_num 2 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + + +// fancy alpha means we expand to keep both premultipied and non-premultiplied color channels +static void stbir__fancy_alpha_weight_4ch( float * out_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) out = out_buffer; + float const * end_decode = out_buffer + ( width_times_channels / 4 ) * 7; // decode buffer aligned to end of out_buffer + float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels; + + // fancy alpha is stored internally as R G B A Rpm Gpm Bpm + + #ifdef STBIR_SIMD + + #ifdef STBIR_SIMD8 + decode += 16; + while ( decode <= end_decode ) + { + stbir__simdf8 d0,d1,a0,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf8_load( d0, decode-16 ); + stbir__simdf8_load( d1, decode-16+8 ); + stbir__simdf8_0123to33333333( a0, d0 ); + stbir__simdf8_0123to33333333( a1, d1 ); + stbir__simdf8_mult( p0, a0, d0 ); + stbir__simdf8_mult( p1, a1, d1 ); + stbir__simdf8_bot4s( a0, d0, p0 ); + stbir__simdf8_bot4s( a1, d1, p1 ); + stbir__simdf8_top4s( d0, d0, p0 ); + stbir__simdf8_top4s( d1, d1, p1 ); + stbir__simdf8_store ( out, a0 ); + stbir__simdf8_store ( out+7, d0 ); + stbir__simdf8_store ( out+14, a1 ); + stbir__simdf8_store ( out+21, d1 ); + decode += 16; + out += 28; + } + decode -= 16; + #else + decode += 8; + while ( decode <= end_decode ) + { + stbir__simdf d0,a0,d1,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf_load( d0, decode-8 ); + stbir__simdf_load( d1, decode-8+4 ); + stbir__simdf_0123to3333( a0, d0 ); + stbir__simdf_0123to3333( a1, d1 ); + stbir__simdf_mult( p0, a0, d0 ); + stbir__simdf_mult( p1, a1, d1 ); + stbir__simdf_store ( out, d0 ); + stbir__simdf_store ( out+4, p0 ); + stbir__simdf_store ( out+7, d1 ); + stbir__simdf_store ( out+7+4, p1 ); + decode += 8; + out += 14; + } + decode -= 8; + #endif + + // might be one last odd pixel + #ifdef STBIR_SIMD8 + while ( decode < end_decode ) + #else + if ( decode < end_decode ) + #endif + { + stbir__simdf d,a,p; + stbir__simdf_load( d, decode ); + stbir__simdf_0123to3333( a, d ); + stbir__simdf_mult( p, a, d ); + stbir__simdf_store ( out, d ); + stbir__simdf_store ( out+4, p ); + decode += 4; + out += 7; + } + + #else + + while( decode < end_decode ) + { + float r = decode[0], g = decode[1], b = decode[2], alpha = decode[3]; + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = alpha; + out[4] = r * alpha; + out[5] = g * alpha; + out[6] = b * alpha; + out += 7; + decode += 4; + } + + #endif +} + +static void stbir__fancy_alpha_weight_2ch( float * out_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) out = out_buffer; + float const * end_decode = out_buffer + ( width_times_channels / 2 ) * 3; + float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels; + + // for fancy alpha, turns into: [X A Xpm][X A Xpm],etc + + #ifdef STBIR_SIMD + + decode += 8; + if ( decode <= end_decode ) + { + do { + #ifdef STBIR_SIMD8 + stbir__simdf8 d0,a0,p0; + STBIR_NO_UNROLL(decode); + stbir__simdf8_load( d0, decode-8 ); + stbir__simdf8_0123to11331133( p0, d0 ); + stbir__simdf8_0123to00220022( a0, d0 ); + stbir__simdf8_mult( p0, p0, a0 ); + + stbir__simdf_store2( out, stbir__if_simdf8_cast_to_simdf4( d0 ) ); + stbir__simdf_store( out+2, stbir__if_simdf8_cast_to_simdf4( p0 ) ); + stbir__simdf_store2h( out+3, stbir__if_simdf8_cast_to_simdf4( d0 ) ); + + stbir__simdf_store2( out+6, stbir__simdf8_gettop4( d0 ) ); + stbir__simdf_store( out+8, stbir__simdf8_gettop4( p0 ) ); + stbir__simdf_store2h( out+9, stbir__simdf8_gettop4( d0 ) ); + #else + stbir__simdf d0,a0,d1,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf_load( d0, decode-8 ); + stbir__simdf_load( d1, decode-8+4 ); + stbir__simdf_0123to1133( p0, d0 ); + stbir__simdf_0123to1133( p1, d1 ); + stbir__simdf_0123to0022( a0, d0 ); + stbir__simdf_0123to0022( a1, d1 ); + stbir__simdf_mult( p0, p0, a0 ); + stbir__simdf_mult( p1, p1, a1 ); + + stbir__simdf_store2( out, d0 ); + stbir__simdf_store( out+2, p0 ); + stbir__simdf_store2h( out+3, d0 ); + + stbir__simdf_store2( out+6, d1 ); + stbir__simdf_store( out+8, p1 ); + stbir__simdf_store2h( out+9, d1 ); + #endif + decode += 8; + out += 12; + } while ( decode <= end_decode ); + } + decode -= 8; + #endif + + while( decode < end_decode ) + { + float x = decode[0], y = decode[1]; + STBIR_SIMD_NO_UNROLL(decode); + out[0] = x; + out[1] = y; + out[2] = x * y; + out += 3; + decode += 2; + } +} + +static void stbir__fancy_alpha_unweight_4ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + // fancy RGBA is stored internally as R G B A Rpm Gpm Bpm + + do { + float alpha = input[3]; +#ifdef STBIR_SIMD + stbir__simdf i,ia; + STBIR_SIMD_NO_UNROLL(encode); + if ( alpha < stbir__small_float ) + { + stbir__simdf_load( i, input ); + stbir__simdf_store( encode, i ); + } + else + { + stbir__simdf_load1frep4( ia, 1.0f / alpha ); + stbir__simdf_load( i, input+4 ); + stbir__simdf_mult( i, i, ia ); + stbir__simdf_store( encode, i ); + encode[3] = alpha; + } +#else + if ( alpha < stbir__small_float ) + { + encode[0] = input[0]; + encode[1] = input[1]; + encode[2] = input[2]; + } + else + { + float ialpha = 1.0f / alpha; + encode[0] = input[4] * ialpha; + encode[1] = input[5] * ialpha; + encode[2] = input[6] * ialpha; + } + encode[3] = alpha; +#endif + + input += 7; + encode += 4; + } while ( encode < end_output ); +} + +// format: [X A Xpm][X A Xpm] etc +static void stbir__fancy_alpha_unweight_2ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = input[1]; + encode[0] = input[0]; + if ( alpha >= stbir__small_float ) + encode[0] = input[2] / alpha; + encode[1] = alpha; + + input += 3; + encode += 2; + } while ( encode < end_output ); +} + +static void stbir__simple_alpha_weight_4ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + #ifdef STBIR_SIMD + { + decode += 2 * stbir__simdfX_float_count; + while ( decode <= end_decode ) + { + stbir__simdfX d0,a0,d1,a1; + STBIR_NO_UNROLL(decode); + stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count ); + stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count ); + stbir__simdfX_aaa1( a0, d0, STBIR_onesX ); + stbir__simdfX_aaa1( a1, d1, STBIR_onesX ); + stbir__simdfX_mult( d0, d0, a0 ); + stbir__simdfX_mult( d1, d1, a1 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 ); + decode += 2 * stbir__simdfX_float_count; + } + decode -= 2 * stbir__simdfX_float_count; + + // few last pixels remnants + #ifdef STBIR_SIMD8 + while ( decode < end_decode ) + #else + if ( decode < end_decode ) + #endif + { + stbir__simdf d,a; + stbir__simdf_load( d, decode ); + stbir__simdf_aaa1( a, d, STBIR__CONSTF(STBIR_ones) ); + stbir__simdf_mult( d, d, a ); + stbir__simdf_store ( decode, d ); + decode += 4; + } + } + + #else + + while( decode < end_decode ) + { + float alpha = decode[3]; + decode[0] *= alpha; + decode[1] *= alpha; + decode[2] *= alpha; + decode += 4; + } + + #endif +} + +static void stbir__simple_alpha_weight_2ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + #ifdef STBIR_SIMD + decode += 2 * stbir__simdfX_float_count; + while ( decode <= end_decode ) + { + stbir__simdfX d0,a0,d1,a1; + STBIR_NO_UNROLL(decode); + stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count ); + stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count ); + stbir__simdfX_a1a1( a0, d0, STBIR_onesX ); + stbir__simdfX_a1a1( a1, d1, STBIR_onesX ); + stbir__simdfX_mult( d0, d0, a0 ); + stbir__simdfX_mult( d1, d1, a1 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 ); + decode += 2 * stbir__simdfX_float_count; + } + decode -= 2 * stbir__simdfX_float_count; + #endif + + while( decode < end_decode ) + { + float alpha = decode[1]; + STBIR_SIMD_NO_UNROLL(decode); + decode[0] *= alpha; + decode += 2; + } +} + +static void stbir__simple_alpha_unweight_4ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = encode[3]; + +#ifdef STBIR_SIMD + stbir__simdf i,ia; + STBIR_SIMD_NO_UNROLL(encode); + if ( alpha >= stbir__small_float ) + { + stbir__simdf_load1frep4( ia, 1.0f / alpha ); + stbir__simdf_load( i, encode ); + stbir__simdf_mult( i, i, ia ); + stbir__simdf_store( encode, i ); + encode[3] = alpha; + } +#else + if ( alpha >= stbir__small_float ) + { + float ialpha = 1.0f / alpha; + encode[0] *= ialpha; + encode[1] *= ialpha; + encode[2] *= ialpha; + } +#endif + encode += 4; + } while ( encode < end_output ); +} + +static void stbir__simple_alpha_unweight_2ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = encode[1]; + if ( alpha >= stbir__small_float ) + encode[0] /= alpha; + encode += 2; + } while ( encode < end_output ); +} + + +// only used in RGB->BGR or BGR->RGB +static void stbir__simple_flip_3ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + decode += 12; + while( decode <= end_decode ) + { + float t0,t1,t2,t3; + STBIR_NO_UNROLL(decode); + t0 = decode[0]; t1 = decode[3]; t2 = decode[6]; t3 = decode[9]; + decode[0] = decode[2]; decode[3] = decode[5]; decode[6] = decode[8]; decode[9] = decode[11]; + decode[2] = t0; decode[5] = t1; decode[8] = t2; decode[11] = t3; + decode += 12; + } + decode -= 12; + + while( decode < end_decode ) + { + float t = decode[0]; + STBIR_NO_UNROLL(decode); + decode[0] = decode[2]; + decode[2] = t; + decode += 3; + } +} + + + +static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float * output_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + int channels = stbir_info->channels; + int effective_channels = stbir_info->effective_channels; + int input_sample_in_bytes = stbir__type_size[stbir_info->input_type] * channels; + stbir_edge edge_horizontal = stbir_info->horizontal.edge; + stbir_edge edge_vertical = stbir_info->vertical.edge; + int row = stbir__edge_wrap(edge_vertical, n, stbir_info->vertical.scale_info.input_full_size); + const void* input_plane_data = ( (char *) stbir_info->input_data ) + (ptrdiff_t)row * (ptrdiff_t) stbir_info->input_stride_bytes; + stbir__span const * spans = stbir_info->scanline_extents.spans; + float* full_decode_buffer = output_buffer - stbir_info->scanline_extents.conservative.n0 * effective_channels; + + // if we are on edge_zero, and we get in here with an out of bounds n, then the calculate filters has failed + STBIR_ASSERT( !(edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->vertical.scale_info.input_full_size)) ); + + do + { + float * decode_buffer; + void const * input_data; + float * end_decode; + int width_times_channels; + int width; + + if ( spans->n1 < spans->n0 ) + break; + + width = spans->n1 + 1 - spans->n0; + decode_buffer = full_decode_buffer + spans->n0 * effective_channels; + end_decode = full_decode_buffer + ( spans->n1 + 1 ) * effective_channels; + width_times_channels = width * channels; + + // read directly out of input plane by default + input_data = ( (char*)input_plane_data ) + spans->pixel_offset_for_input * input_sample_in_bytes; + + // if we have an input callback, call it to get the input data + if ( stbir_info->in_pixels_cb ) + { + // call the callback with a temp buffer (that they can choose to use or not). the temp is just right aligned memory in the decode_buffer itself + input_data = stbir_info->in_pixels_cb( ( (char*) end_decode ) - ( width * input_sample_in_bytes ), input_plane_data, width, spans->pixel_offset_for_input, row, stbir_info->user_data ); + } + + STBIR_PROFILE_START( decode ); + // convert the pixels info the float decode_buffer, (we index from end_decode, so that when channelsdecode_pixels( (float*)end_decode - width_times_channels, width_times_channels, input_data ); + STBIR_PROFILE_END( decode ); + + if (stbir_info->alpha_weight) + { + STBIR_PROFILE_START( alpha ); + stbir_info->alpha_weight( decode_buffer, width_times_channels ); + STBIR_PROFILE_END( alpha ); + } + + ++spans; + } while ( spans <= ( &stbir_info->scanline_extents.spans[1] ) ); + + // handle the edge_wrap filter (all other types are handled back out at the calculate_filter stage) + // basically the idea here is that if we have the whole scanline in memory, we don't redecode the + // wrapped edge pixels, and instead just memcpy them from the scanline into the edge positions + if ( ( edge_horizontal == STBIR_EDGE_WRAP ) && ( stbir_info->scanline_extents.edge_sizes[0] | stbir_info->scanline_extents.edge_sizes[1] ) ) + { + // this code only runs if we're in edge_wrap, and we're doing the entire scanline + int e, start_x[2]; + int input_full_size = stbir_info->horizontal.scale_info.input_full_size; + + start_x[0] = -stbir_info->scanline_extents.edge_sizes[0]; // left edge start x + start_x[1] = input_full_size; // right edge + + for( e = 0; e < 2 ; e++ ) + { + // do each margin + int margin = stbir_info->scanline_extents.edge_sizes[e]; + if ( margin ) + { + int x = start_x[e]; + float * marg = full_decode_buffer + x * effective_channels; + float const * src = full_decode_buffer + stbir__edge_wrap(edge_horizontal, x, input_full_size) * effective_channels; + STBIR_MEMCPY( marg, src, margin * effective_channels * sizeof(float) ); + } + } + } +} + + +//================= +// Do 1 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_mult1_mem( tot, c, decode ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2z( c, hc ); \ + stbir__simdf_load2( d, decode ); \ + stbir__simdf_mult( tot, c, d ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_add1( tot, tot, c ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_0123to2301( t, tot ); \ + stbir__simdf_add1( tot, tot, c ); \ + stbir__simdf_add1( tot, tot, t ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store1( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#define stbir__4_coeff_start() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc + (ofs) ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load1z( c, hc + (ofs) ); \ + stbir__simdf_load1( d, decode + (ofs) ); \ + stbir__simdf_madd( tot, tot, d, c ); } + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load2z( c, hc+(ofs) ); \ + stbir__simdf_load2( d, decode+(ofs) ); \ + stbir__simdf_madd( tot, tot, d, c ); } + +#define stbir__3_coeff_setup() \ + stbir__simdf mask; \ + stbir__simdf_load( mask, STBIR_mask + 3 ); + +#define stbir__3_coeff_remnant( ofs ) \ + stbir__simdf_load( c, hc+(ofs) ); \ + stbir__simdf_and( c, c, mask ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) ); + +#define stbir__store_output() \ + stbir__simdf_0123to2301( c, tot ); \ + stbir__simdf_add( tot, tot, c ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_add1( tot, tot, c ); \ + stbir__simdf_store1( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#else + +#define stbir__1_coeff_only() \ + float tot; \ + tot = decode[0]*hc[0]; + +#define stbir__2_coeff_only() \ + float tot; \ + tot = decode[0] * hc[0]; \ + tot += decode[1] * hc[1]; + +#define stbir__3_coeff_only() \ + float tot; \ + tot = decode[0] * hc[0]; \ + tot += decode[1] * hc[1]; \ + tot += decode[2] * hc[2]; + +#define stbir__store_output_tiny() \ + output[0] = tot; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#define stbir__4_coeff_start() \ + float tot0,tot1,tot2,tot3; \ + tot0 = decode[0] * hc[0]; \ + tot1 = decode[1] * hc[1]; \ + tot2 = decode[2] * hc[2]; \ + tot3 = decode[3] * hc[3]; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + tot2 += decode[2+(ofs)] * hc[2+(ofs)]; \ + tot3 += decode[3+(ofs)] * hc[3+(ofs)]; + +#define stbir__1_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; + +#define stbir__2_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + +#define stbir__3_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + tot2 += decode[2+(ofs)] * hc[2+(ofs)]; + +#define stbir__store_output() \ + output[0] = (tot0+tot2)+(tot1+tot3); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#endif + +#define STBIR__horizontal_channels 1 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +//================= +// Do 2 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc ); \ + stbir__simdf_0123to0011( c, c ); \ + stbir__simdf_load2( d, decode ); \ + stbir__simdf_mult( tot, d, c ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( c, hc ); \ + stbir__simdf_0123to0011( c, c ); \ + stbir__simdf_mult_mem( tot, c, decode ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,cs,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load2z( d, decode+4 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__store_output_tiny() \ + stbir__simdf_0123to2301( c, tot ); \ + stbir__simdf_add( tot, tot, c ); \ + stbir__simdf_store2( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf t; \ + stbir__simdf_load1z( t, hc + (ofs) ); \ + stbir__simdf_0123to0011( t, t ); \ + stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \ + stbir__simdf8_add4( tot0, tot0, t ); } + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf t; \ + stbir__simdf_load2( t, hc + (ofs) ); \ + stbir__simdf_0123to0011( t, t ); \ + stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \ + stbir__simdf8_add4( tot0, tot0, t ); } + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf8 d; \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_load6z( d, decode+(ofs)*2 ); \ + stbir__simdf8_madd( tot0, tot0, c, d ); } + +#define stbir__store_output() \ + { stbir__simdf t,c; \ + stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \ + stbir__simdf_0123to2301( c, t ); \ + stbir__simdf_add( t, t, c ); \ + stbir__simdf_store2( output, t ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; } + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to2233( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \ + stbir__simdf_0123to2233( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*2+4 ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load1z( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_load2( d, decode + (ofs) * 2 ); \ + stbir__simdf_madd( tot0, tot0, d, c ); } + +#define stbir__2_coeff_remnant( ofs ) \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load2z( d, decode + (ofs) * 2 + 4 ); \ + stbir__simdf_madd( tot1, tot1, d, c ); } + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot1 ); \ + stbir__simdf_0123to2301( c, tot0 ); \ + stbir__simdf_add( tot0, tot0, c ); \ + stbir__simdf_store2( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; + +#define stbir__2_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; \ + c = hc[1]; \ + tota += decode[2]*c; \ + totb += decode[3]*c; + +// this weird order of add matches the simd +#define stbir__3_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; \ + c = hc[2]; \ + tota += decode[4]*c; \ + totb += decode[5]*c; \ + c = hc[1]; \ + tota += decode[2]*c; \ + totb += decode[3]*c; + +#define stbir__store_output_tiny() \ + output[0] = tota; \ + output[1] = totb; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#define stbir__4_coeff_start() \ + float tota0,tota1,tota2,tota3,totb0,totb1,totb2,totb3,c; \ + c = hc[0]; \ + tota0 = decode[0]*c; \ + totb0 = decode[1]*c; \ + c = hc[1]; \ + tota1 = decode[2]*c; \ + totb1 = decode[3]*c; \ + c = hc[2]; \ + tota2 = decode[4]*c; \ + totb2 = decode[5]*c; \ + c = hc[3]; \ + tota3 = decode[6]*c; \ + totb3 = decode[7]*c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2]*c; \ + totb0 += decode[1+(ofs)*2]*c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2]*c; \ + totb1 += decode[3+(ofs)*2]*c; \ + c = hc[2+(ofs)]; \ + tota2 += decode[4+(ofs)*2]*c; \ + totb2 += decode[5+(ofs)*2]*c; \ + c = hc[3+(ofs)]; \ + tota3 += decode[6+(ofs)*2]*c; \ + totb3 += decode[7+(ofs)*2]*c; + +#define stbir__1_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; + +#define stbir__2_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2] * c; \ + totb1 += decode[3+(ofs)*2] * c; + +#define stbir__3_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2] * c; \ + totb1 += decode[3+(ofs)*2] * c; \ + c = hc[2+(ofs)]; \ + tota2 += decode[4+(ofs)*2] * c; \ + totb2 += decode[5+(ofs)*2] * c; + +#define stbir__store_output() \ + output[0] = (tota0+tota2)+(tota1+tota3); \ + output[1] = (totb0+totb2)+(totb1+totb3); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#endif + +#define STBIR__horizontal_channels 2 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +//================= +// Do 3 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc ); \ + stbir__simdf_0123to0001( c, c ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,cs,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_load( d, decode+3 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,d,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_load( d, decode+3 ); \ + stbir__simdf_madd( tot, tot, d, c ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load( d, decode+6 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store2( output, tot ); \ + stbir__simdf_0123to2301( tot, tot ); \ + stbir__simdf_store1( output+2, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#ifdef STBIR_SIMD8 + +// we're loading from the XXXYYY decode by -1 to get the XXXYYY into different halves of the AVX reg fyi +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,tot1,c,cs; stbir__simdf t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode - 1 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_mult_mem( tot1, c, decode+6 - 1 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*3 + 6 - 1 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1rep4( t, hc + (ofs) ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*3 - 1 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); + + #define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \ + stbir__simdf8_0123to2222( t, cs ); \ + stbir__simdf8_madd_mem4( tot1, tot1, t, decode+(ofs)*3 + 6 - 1 ); + +#define stbir__store_output() \ + stbir__simdf8_add( tot0, tot0, tot1 ); \ + stbir__simdf_0123to1230( t, stbir__if_simdf8_cast_to_simdf4( tot0 ) ); \ + stbir__simdf8_add4halves( t, t, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; \ + if ( output < output_end ) \ + { \ + stbir__simdf_store( output-3, t ); \ + continue; \ + } \ + { stbir__simdf tt; stbir__simdf_0123to2301( tt, t ); \ + stbir__simdf_store2( output-3, t ); \ + stbir__simdf_store1( output+2-3, tt ); } \ + break; + + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,tot2,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); \ + stbir__simdf_0123to2333( c, cs ); \ + stbir__simdf_mult_mem( tot2, c, decode+8 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \ + stbir__simdf_0123to2333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*3+8 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2z( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_load2z( d, decode+(ofs)*3+4 ); \ + stbir__simdf_madd( tot1, tot1, c, d ); } + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load1z( d, decode+(ofs)*3+8 ); \ + stbir__simdf_madd( tot2, tot2, c, d ); } + +#define stbir__store_output() \ + stbir__simdf_0123ABCDto3ABx( c, tot0, tot1 ); \ + stbir__simdf_0123ABCDto23Ax( cs, tot1, tot2 ); \ + stbir__simdf_0123to1230( tot2, tot2 ); \ + stbir__simdf_add( tot0, tot0, cs ); \ + stbir__simdf_add( c, c, tot2 ); \ + stbir__simdf_add( tot0, tot0, c ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; \ + if ( output < output_end ) \ + { \ + stbir__simdf_store( output-3, tot0 ); \ + continue; \ + } \ + stbir__simdf_0123to2301( tot1, tot0 ); \ + stbir__simdf_store2( output-3, tot0 ); \ + stbir__simdf_store1( output+2-3, tot1 ); \ + break; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; + +#define stbir__2_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + c = hc[1]; \ + tot0 += decode[3]*c; \ + tot1 += decode[4]*c; \ + tot2 += decode[5]*c; + +#define stbir__3_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + c = hc[1]; \ + tot0 += decode[3]*c; \ + tot1 += decode[4]*c; \ + tot2 += decode[5]*c; \ + c = hc[2]; \ + tot0 += decode[6]*c; \ + tot1 += decode[7]*c; \ + tot2 += decode[8]*c; + +#define stbir__store_output_tiny() \ + output[0] = tot0; \ + output[1] = tot1; \ + output[2] = tot2; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#define stbir__4_coeff_start() \ + float tota0,tota1,tota2,totb0,totb1,totb2,totc0,totc1,totc2,totd0,totd1,totd2,c; \ + c = hc[0]; \ + tota0 = decode[0]*c; \ + tota1 = decode[1]*c; \ + tota2 = decode[2]*c; \ + c = hc[1]; \ + totb0 = decode[3]*c; \ + totb1 = decode[4]*c; \ + totb2 = decode[5]*c; \ + c = hc[2]; \ + totc0 = decode[6]*c; \ + totc1 = decode[7]*c; \ + totc2 = decode[8]*c; \ + c = hc[3]; \ + totd0 = decode[9]*c; \ + totd1 = decode[10]*c; \ + totd2 = decode[11]*c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + c = hc[2+(ofs)]; \ + totc0 += decode[6+(ofs)*3]*c; \ + totc1 += decode[7+(ofs)*3]*c; \ + totc2 += decode[8+(ofs)*3]*c; \ + c = hc[3+(ofs)]; \ + totd0 += decode[9+(ofs)*3]*c; \ + totd1 += decode[10+(ofs)*3]*c; \ + totd2 += decode[11+(ofs)*3]*c; + +#define stbir__1_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; + +#define stbir__2_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + +#define stbir__3_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + c = hc[2+(ofs)]; \ + totc0 += decode[6+(ofs)*3]*c; \ + totc1 += decode[7+(ofs)*3]*c; \ + totc2 += decode[8+(ofs)*3]*c; + +#define stbir__store_output() \ + output[0] = (tota0+totc0)+(totb0+totd0); \ + output[1] = (tota1+totc1)+(totb1+totd1); \ + output[2] = (tota2+totc2)+(totb2+totd2); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#endif + +#define STBIR__horizontal_channels 3 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + +//================= +// Do 4 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_mult_mem( tot, c, decode ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+4 ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+8 ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,c,cs; stbir__simdf t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+8 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1rep4( t, hc + (ofs) ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); + + #define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf8_0123to2222( t, cs ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4+8 ); + +#define stbir__store_output() \ + stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \ + stbir__simdf_store( output, t ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+8 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+12 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+12 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; + +#define stbir__2_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; \ + c = hc[1]; \ + p0 += decode[4] * c; \ + p1 += decode[5] * c; \ + p2 += decode[6] * c; \ + p3 += decode[7] * c; + +#define stbir__3_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; \ + c = hc[1]; \ + p0 += decode[4] * c; \ + p1 += decode[5] * c; \ + p2 += decode[6] * c; \ + p3 += decode[7] * c; \ + c = hc[2]; \ + p0 += decode[8] * c; \ + p1 += decode[9] * c; \ + p2 += decode[10] * c; \ + p3 += decode[11] * c; + +#define stbir__store_output_tiny() \ + output[0] = p0; \ + output[1] = p1; \ + output[2] = p2; \ + output[3] = p3; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#define stbir__4_coeff_start() \ + float x0,x1,x2,x3,y0,y1,y2,y3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + x0 = decode[0] * c; \ + x1 = decode[1] * c; \ + x2 = decode[2] * c; \ + x3 = decode[3] * c; \ + c = hc[1]; \ + y0 = decode[4] * c; \ + y1 = decode[5] * c; \ + y2 = decode[6] * c; \ + y3 = decode[7] * c; \ + c = hc[2]; \ + x0 += decode[8] * c; \ + x1 += decode[9] * c; \ + x2 += decode[10] * c; \ + x3 += decode[11] * c; \ + c = hc[3]; \ + y0 += decode[12] * c; \ + y1 += decode[13] * c; \ + y2 += decode[14] * c; \ + y3 += decode[15] * c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[8+(ofs)*4] * c; \ + x1 += decode[9+(ofs)*4] * c; \ + x2 += decode[10+(ofs)*4] * c; \ + x3 += decode[11+(ofs)*4] * c; \ + c = hc[3+(ofs)]; \ + y0 += decode[12+(ofs)*4] * c; \ + y1 += decode[13+(ofs)*4] * c; \ + y2 += decode[14+(ofs)*4] * c; \ + y3 += decode[15+(ofs)*4] * c; + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[8+(ofs)*4] * c; \ + x1 += decode[9+(ofs)*4] * c; \ + x2 += decode[10+(ofs)*4] * c; \ + x3 += decode[11+(ofs)*4] * c; + +#define stbir__store_output() \ + output[0] = x0 + y0; \ + output[1] = x1 + y1; \ + output[2] = x2 + y2; \ + output[3] = x3 + y3; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#endif + +#define STBIR__horizontal_channels 4 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + + +//================= +// Do 7 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot0,tot1,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c,decode+10 ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store( output+3, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_mult_mem( tot1, c, decode+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf8_0123to33333333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+21 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf8_0123to33333333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+21 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load1b( c, hc + (ofs) ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load1b( c, hc + (ofs) ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_load1b( c, hc + (ofs)+1 ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); + +#define stbir__store_output() \ + stbir__simdf8_add( tot0, tot0, tot1 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; \ + if ( output < output_end ) \ + { \ + stbir__simdf8_store( output-7, tot0 ); \ + continue; \ + } \ + stbir__simdf_store( output-7+3, stbir__simdf_swiz(stbir__simdf8_gettop4(tot0),0,0,1,2) ); \ + stbir__simdf_store( output-7, stbir__if_simdf8_cast_to_simdf4(tot0) ); \ + break; + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,tot2,tot3,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_mult_mem( tot2, c, decode+7 ); \ + stbir__simdf_mult_mem( tot3, c, decode+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+21 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+24 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+21 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+24 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot2 ); \ + stbir__simdf_add( tot1, tot1, tot3 ); \ + stbir__simdf_store( output+3, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; + +#define stbir__2_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; \ + c = hc[1]; \ + tot0 += decode[7]*c; \ + tot1 += decode[8]*c; \ + tot2 += decode[9]*c; \ + tot3 += decode[10]*c; \ + tot4 += decode[11]*c; \ + tot5 += decode[12]*c; \ + tot6 += decode[13]*c; \ + +#define stbir__3_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; \ + c = hc[1]; \ + tot0 += decode[7]*c; \ + tot1 += decode[8]*c; \ + tot2 += decode[9]*c; \ + tot3 += decode[10]*c; \ + tot4 += decode[11]*c; \ + tot5 += decode[12]*c; \ + tot6 += decode[13]*c; \ + c = hc[2]; \ + tot0 += decode[14]*c; \ + tot1 += decode[15]*c; \ + tot2 += decode[16]*c; \ + tot3 += decode[17]*c; \ + tot4 += decode[18]*c; \ + tot5 += decode[19]*c; \ + tot6 += decode[20]*c; \ + +#define stbir__store_output_tiny() \ + output[0] = tot0; \ + output[1] = tot1; \ + output[2] = tot2; \ + output[3] = tot3; \ + output[4] = tot4; \ + output[5] = tot5; \ + output[6] = tot6; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#define stbir__4_coeff_start() \ + float x0,x1,x2,x3,x4,x5,x6,y0,y1,y2,y3,y4,y5,y6,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + x0 = decode[0] * c; \ + x1 = decode[1] * c; \ + x2 = decode[2] * c; \ + x3 = decode[3] * c; \ + x4 = decode[4] * c; \ + x5 = decode[5] * c; \ + x6 = decode[6] * c; \ + c = hc[1]; \ + y0 = decode[7] * c; \ + y1 = decode[8] * c; \ + y2 = decode[9] * c; \ + y3 = decode[10] * c; \ + y4 = decode[11] * c; \ + y5 = decode[12] * c; \ + y6 = decode[13] * c; \ + c = hc[2]; \ + x0 += decode[14] * c; \ + x1 += decode[15] * c; \ + x2 += decode[16] * c; \ + x3 += decode[17] * c; \ + x4 += decode[18] * c; \ + x5 += decode[19] * c; \ + x6 += decode[20] * c; \ + c = hc[3]; \ + y0 += decode[21] * c; \ + y1 += decode[22] * c; \ + y2 += decode[23] * c; \ + y3 += decode[24] * c; \ + y4 += decode[25] * c; \ + y5 += decode[26] * c; \ + y6 += decode[27] * c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[14+(ofs)*7] * c; \ + x1 += decode[15+(ofs)*7] * c; \ + x2 += decode[16+(ofs)*7] * c; \ + x3 += decode[17+(ofs)*7] * c; \ + x4 += decode[18+(ofs)*7] * c; \ + x5 += decode[19+(ofs)*7] * c; \ + x6 += decode[20+(ofs)*7] * c; \ + c = hc[3+(ofs)]; \ + y0 += decode[21+(ofs)*7] * c; \ + y1 += decode[22+(ofs)*7] * c; \ + y2 += decode[23+(ofs)*7] * c; \ + y3 += decode[24+(ofs)*7] * c; \ + y4 += decode[25+(ofs)*7] * c; \ + y5 += decode[26+(ofs)*7] * c; \ + y6 += decode[27+(ofs)*7] * c; + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[14+(ofs)*7] * c; \ + x1 += decode[15+(ofs)*7] * c; \ + x2 += decode[16+(ofs)*7] * c; \ + x3 += decode[17+(ofs)*7] * c; \ + x4 += decode[18+(ofs)*7] * c; \ + x5 += decode[19+(ofs)*7] * c; \ + x6 += decode[20+(ofs)*7] * c; \ + +#define stbir__store_output() \ + output[0] = x0 + y0; \ + output[1] = x1 + y1; \ + output[2] = x2 + y2; \ + output[3] = x3 + y3; \ + output[4] = x4 + y4; \ + output[5] = x5 + y5; \ + output[6] = x6 + y6; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#endif + +#define STBIR__horizontal_channels 7 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +// include all of the vertical resamplers (both scatter and gather versions) + +#define STBIR__vertical_channels 1 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 1 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 2 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 2 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 3 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 3 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 4 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 4 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 5 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 5 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 6 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 6 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 7 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 7 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 8 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 8 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +typedef void STBIR_VERTICAL_GATHERFUNC( float * output, float const * coeffs, float const ** inputs, float const * input0_end ); + +static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers[ 8 ] = +{ + stbir__vertical_gather_with_1_coeffs,stbir__vertical_gather_with_2_coeffs,stbir__vertical_gather_with_3_coeffs,stbir__vertical_gather_with_4_coeffs,stbir__vertical_gather_with_5_coeffs,stbir__vertical_gather_with_6_coeffs,stbir__vertical_gather_with_7_coeffs,stbir__vertical_gather_with_8_coeffs +}; + +static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers_continues[ 8 ] = +{ + stbir__vertical_gather_with_1_coeffs_cont,stbir__vertical_gather_with_2_coeffs_cont,stbir__vertical_gather_with_3_coeffs_cont,stbir__vertical_gather_with_4_coeffs_cont,stbir__vertical_gather_with_5_coeffs_cont,stbir__vertical_gather_with_6_coeffs_cont,stbir__vertical_gather_with_7_coeffs_cont,stbir__vertical_gather_with_8_coeffs_cont +}; + +typedef void STBIR_VERTICAL_SCATTERFUNC( float ** outputs, float const * coeffs, float const * input, float const * input_end ); + +static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_sets[ 8 ] = +{ + stbir__vertical_scatter_with_1_coeffs,stbir__vertical_scatter_with_2_coeffs,stbir__vertical_scatter_with_3_coeffs,stbir__vertical_scatter_with_4_coeffs,stbir__vertical_scatter_with_5_coeffs,stbir__vertical_scatter_with_6_coeffs,stbir__vertical_scatter_with_7_coeffs,stbir__vertical_scatter_with_8_coeffs +}; + +static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_blends[ 8 ] = +{ + stbir__vertical_scatter_with_1_coeffs_cont,stbir__vertical_scatter_with_2_coeffs_cont,stbir__vertical_scatter_with_3_coeffs_cont,stbir__vertical_scatter_with_4_coeffs_cont,stbir__vertical_scatter_with_5_coeffs_cont,stbir__vertical_scatter_with_6_coeffs_cont,stbir__vertical_scatter_with_7_coeffs_cont,stbir__vertical_scatter_with_8_coeffs_cont +}; + + +static void stbir__encode_scanline( stbir__info const * stbir_info, void *output_buffer_data, float * encode_buffer, int row STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + int num_pixels = stbir_info->horizontal.scale_info.output_sub_size; + int channels = stbir_info->channels; + int width_times_channels = num_pixels * channels; + void * output_buffer; + + // un-alpha weight if we need to + if ( stbir_info->alpha_unweight ) + { + STBIR_PROFILE_START( unalpha ); + stbir_info->alpha_unweight( encode_buffer, width_times_channels ); + STBIR_PROFILE_END( unalpha ); + } + + // write directly into output by default + output_buffer = output_buffer_data; + + // if we have an output callback, we first convert the decode buffer in place (and then hand that to the callback) + if ( stbir_info->out_pixels_cb ) + output_buffer = encode_buffer; + + STBIR_PROFILE_START( encode ); + // convert into the output buffer + stbir_info->encode_pixels( output_buffer, width_times_channels, encode_buffer ); + STBIR_PROFILE_END( encode ); + + // if we have an output callback, call it to send the data + if ( stbir_info->out_pixels_cb ) + stbir_info->out_pixels_cb( output_buffer_data, num_pixels, row, stbir_info->user_data ); +} + + +// Get the ring buffer pointer for an index +static float* stbir__get_ring_buffer_entry(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int index ) +{ + STBIR_ASSERT( index < stbir_info->ring_buffer_num_entries ); + + #ifdef STBIR__SEPARATE_ALLOCATIONS + return split_info->ring_buffers[ index ]; + #else + return (float*) ( ( (char*) split_info->ring_buffer ) + ( index * stbir_info->ring_buffer_length_bytes ) ); + #endif +} + +// Get the specified scan line from the ring buffer +static float* stbir__get_ring_buffer_scanline(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int get_scanline) +{ + int ring_buffer_index = (split_info->ring_buffer_begin_index + (get_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + return stbir__get_ring_buffer_entry( stbir_info, split_info, ring_buffer_index ); +} + +static void stbir__resample_horizontal_gather(stbir__info const * stbir_info, float* output_buffer, float const * input_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + float const * decode_buffer = input_buffer - ( stbir_info->scanline_extents.conservative.n0 * stbir_info->effective_channels ); + + STBIR_PROFILE_START( horizontal ); + if ( ( stbir_info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( stbir_info->horizontal.scale_info.scale == 1.0f ) ) + STBIR_MEMCPY( output_buffer, input_buffer, stbir_info->horizontal.scale_info.output_sub_size * sizeof( float ) * stbir_info->effective_channels ); + else + stbir_info->horizontal_gather_channels( output_buffer, stbir_info->horizontal.scale_info.output_sub_size, decode_buffer, stbir_info->horizontal.contributors, stbir_info->horizontal.coefficients, stbir_info->horizontal.coefficient_width ); + STBIR_PROFILE_END( horizontal ); +} + +static void stbir__resample_vertical_gather(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n, int contrib_n0, int contrib_n1, float const * vertical_coefficients ) +{ + float* encode_buffer = split_info->vertical_buffer; + float* decode_buffer = split_info->decode_buffer; + int vertical_first = stbir_info->vertical_first; + int width = (vertical_first) ? ( stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1 ) : stbir_info->horizontal.scale_info.output_sub_size; + int width_times_channels = stbir_info->effective_channels * width; + + STBIR_ASSERT( stbir_info->vertical.is_gather ); + + // loop over the contributing scanlines and scale into the buffer + STBIR_PROFILE_START( vertical ); + { + int k = 0, total = contrib_n1 - contrib_n0 + 1; + STBIR_ASSERT( total > 0 ); + do { + float const * inputs[8]; + int i, cnt = total; if ( cnt > 8 ) cnt = 8; + for( i = 0 ; i < cnt ; i++ ) + inputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+contrib_n0 ); + + // call the N scanlines at a time function (up to 8 scanlines of blending at once) + ((k==0)?stbir__vertical_gathers:stbir__vertical_gathers_continues)[cnt-1]( (vertical_first) ? decode_buffer : encode_buffer, vertical_coefficients + k, inputs, inputs[0] + width_times_channels ); + k += cnt; + total -= cnt; + } while ( total ); + } + STBIR_PROFILE_END( vertical ); + + if ( vertical_first ) + { + // Now resample the gathered vertical data in the horizontal axis into the encode buffer + stbir__resample_horizontal_gather(stbir_info, encode_buffer, decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + } + + stbir__encode_scanline( stbir_info, ( (char *) stbir_info->output_data ) + ((ptrdiff_t)n * (ptrdiff_t)stbir_info->output_stride_bytes), + encode_buffer, n STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); +} + +static void stbir__decode_and_resample_for_vertical_gather_loop(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, n, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // update new end scanline + split_info->ring_buffer_last_scanline = n; + + // get ring buffer + ring_buffer_index = (split_info->ring_buffer_begin_index + (split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + ring_buffer = stbir__get_ring_buffer_entry(stbir_info, split_info, ring_buffer_index); + + // Now resample it into the ring buffer. + stbir__resample_horizontal_gather( stbir_info, ring_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__vertical_gather_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count ) +{ + int y, start_output_y, end_output_y; + stbir__contributors* vertical_contributors = stbir_info->vertical.contributors; + float const * vertical_coefficients = stbir_info->vertical.coefficients; + + STBIR_ASSERT( stbir_info->vertical.is_gather ); + + start_output_y = split_info->start_output_y; + end_output_y = split_info[split_count-1].end_output_y; + + vertical_contributors += start_output_y; + vertical_coefficients += start_output_y * stbir_info->vertical.coefficient_width; + + // initialize the ring buffer for gathering + split_info->ring_buffer_begin_index = 0; + split_info->ring_buffer_first_scanline = stbir_info->vertical.extent_info.lowest; + split_info->ring_buffer_last_scanline = split_info->ring_buffer_first_scanline - 1; // means "empty" + + for (y = start_output_y; y < end_output_y; y++) + { + int in_first_scanline, in_last_scanline; + + in_first_scanline = vertical_contributors->n0; + in_last_scanline = vertical_contributors->n1; + + // make sure the indexing hasn't broken + STBIR_ASSERT( in_first_scanline >= split_info->ring_buffer_first_scanline ); + + // Load in new scanlines + while (in_last_scanline > split_info->ring_buffer_last_scanline) + { + STBIR_ASSERT( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) <= stbir_info->ring_buffer_num_entries ); + + // make sure there was room in the ring buffer when we add new scanlines + if ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) + { + split_info->ring_buffer_first_scanline++; + split_info->ring_buffer_begin_index++; + } + + if ( stbir_info->vertical_first ) + { + float * ring_buffer = stbir__get_ring_buffer_scanline( stbir_info, split_info, ++split_info->ring_buffer_last_scanline ); + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, split_info->ring_buffer_last_scanline, ring_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + } + else + { + stbir__decode_and_resample_for_vertical_gather_loop(stbir_info, split_info, split_info->ring_buffer_last_scanline + 1); + } + } + + // Now all buffers should be ready to write a row of vertical sampling, so do it. + stbir__resample_vertical_gather(stbir_info, split_info, y, in_first_scanline, in_last_scanline, vertical_coefficients ); + + ++vertical_contributors; + vertical_coefficients += stbir_info->vertical.coefficient_width; + } +} + +#define STBIR__FLOAT_EMPTY_MARKER 3.0e+38F +#define STBIR__FLOAT_BUFFER_IS_EMPTY(ptr) ((ptr)[0]==STBIR__FLOAT_EMPTY_MARKER) + +static void stbir__encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info) +{ + // evict a scanline out into the output buffer + float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index ); + + // dump the scanline out + stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (ptrdiff_t)split_info->ring_buffer_first_scanline * (ptrdiff_t)stbir_info->output_stride_bytes ), ring_buffer_entry, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // mark it as empty + ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER; + + // advance the first scanline + split_info->ring_buffer_first_scanline++; + if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries ) + split_info->ring_buffer_begin_index = 0; +} + +static void stbir__horizontal_resample_and_encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info) +{ + // evict a scanline out into the output buffer + + float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index ); + + // Now resample it into the buffer. + stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, ring_buffer_entry STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // dump the scanline out + stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (ptrdiff_t)split_info->ring_buffer_first_scanline * (ptrdiff_t)stbir_info->output_stride_bytes ), split_info->vertical_buffer, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // mark it as empty + ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER; + + // advance the first scanline + split_info->ring_buffer_first_scanline++; + if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries ) + split_info->ring_buffer_begin_index = 0; +} + +static void stbir__resample_vertical_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n0, int n1, float const * vertical_coefficients, float const * vertical_buffer, float const * vertical_buffer_end ) +{ + STBIR_ASSERT( !stbir_info->vertical.is_gather ); + + STBIR_PROFILE_START( vertical ); + { + int k = 0, total = n1 - n0 + 1; + STBIR_ASSERT( total > 0 ); + do { + float * outputs[8]; + int i, n = total; if ( n > 8 ) n = 8; + for( i = 0 ; i < n ; i++ ) + { + outputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+n0 ); + if ( ( i ) && ( STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[i] ) != STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ) ) ) // make sure runs are of the same type + { + n = i; + break; + } + } + // call the scatter to N scanlines at a time function (up to 8 scanlines of scattering at once) + ((STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ))?stbir__vertical_scatter_sets:stbir__vertical_scatter_blends)[n-1]( outputs, vertical_coefficients + k, vertical_buffer, vertical_buffer_end ); + k += n; + total -= n; + } while ( total ); + } + + STBIR_PROFILE_END( vertical ); +} + +typedef void stbir__handle_scanline_for_scatter_func(stbir__info const * stbir_info, stbir__per_split_info* split_info); + +static void stbir__vertical_scatter_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count ) +{ + int y, start_output_y, end_output_y, start_input_y, end_input_y; + stbir__contributors* vertical_contributors = stbir_info->vertical.contributors; + float const * vertical_coefficients = stbir_info->vertical.coefficients; + stbir__handle_scanline_for_scatter_func * handle_scanline_for_scatter; + void * scanline_scatter_buffer; + void * scanline_scatter_buffer_end; + int on_first_input_y, last_input_y; + + STBIR_ASSERT( !stbir_info->vertical.is_gather ); + + start_output_y = split_info->start_output_y; + end_output_y = split_info[split_count-1].end_output_y; // may do multiple split counts + + start_input_y = split_info->start_input_y; + end_input_y = split_info[split_count-1].end_input_y; + + // adjust for starting offset start_input_y + y = start_input_y + stbir_info->vertical.filter_pixel_margin; + vertical_contributors += y ; + vertical_coefficients += stbir_info->vertical.coefficient_width * y; + + if ( stbir_info->vertical_first ) + { + handle_scanline_for_scatter = stbir__horizontal_resample_and_encode_first_scanline_from_scatter; + scanline_scatter_buffer = split_info->decode_buffer; + scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * (stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1); + } + else + { + handle_scanline_for_scatter = stbir__encode_first_scanline_from_scatter; + scanline_scatter_buffer = split_info->vertical_buffer; + scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * stbir_info->horizontal.scale_info.output_sub_size; + } + + // initialize the ring buffer for scattering + split_info->ring_buffer_first_scanline = start_output_y; + split_info->ring_buffer_last_scanline = -1; + split_info->ring_buffer_begin_index = -1; + + // mark all the buffers as empty to start + for( y = 0 ; y < stbir_info->ring_buffer_num_entries ; y++ ) + stbir__get_ring_buffer_entry( stbir_info, split_info, y )[0] = STBIR__FLOAT_EMPTY_MARKER; // only used on scatter + + // do the loop in input space + on_first_input_y = 1; last_input_y = start_input_y; + for (y = start_input_y ; y < end_input_y; y++) + { + int out_first_scanline, out_last_scanline; + + out_first_scanline = vertical_contributors->n0; + out_last_scanline = vertical_contributors->n1; + + STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if ( ( out_last_scanline >= out_first_scanline ) && ( ( ( out_first_scanline >= start_output_y ) && ( out_first_scanline < end_output_y ) ) || ( ( out_last_scanline >= start_output_y ) && ( out_last_scanline < end_output_y ) ) ) ) + { + float const * vc = vertical_coefficients; + + // keep track of the range actually seen for the next resize + last_input_y = y; + if ( ( on_first_input_y ) && ( y > start_input_y ) ) + split_info->start_input_y = y; + on_first_input_y = 0; + + // clip the region + if ( out_first_scanline < start_output_y ) + { + vc += start_output_y - out_first_scanline; + out_first_scanline = start_output_y; + } + + if ( out_last_scanline >= end_output_y ) + out_last_scanline = end_output_y - 1; + + // if very first scanline, init the index + if (split_info->ring_buffer_begin_index < 0) + split_info->ring_buffer_begin_index = out_first_scanline - start_output_y; + + STBIR_ASSERT( split_info->ring_buffer_begin_index <= out_first_scanline ); + + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, y, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // When horizontal first, we resample horizontally into the vertical buffer before we scatter it out + if ( !stbir_info->vertical_first ) + stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // Now it's sitting in the buffer ready to be distributed into the ring buffers. + + // evict from the ringbuffer, if we need are full + if ( ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) && + ( out_last_scanline > split_info->ring_buffer_last_scanline ) ) + handle_scanline_for_scatter( stbir_info, split_info ); + + // Now the horizontal buffer is ready to write to all ring buffer rows, so do it. + stbir__resample_vertical_scatter(stbir_info, split_info, out_first_scanline, out_last_scanline, vc, (float*)scanline_scatter_buffer, (float*)scanline_scatter_buffer_end ); + + // update the end of the buffer + if ( out_last_scanline > split_info->ring_buffer_last_scanline ) + split_info->ring_buffer_last_scanline = out_last_scanline; + } + ++vertical_contributors; + vertical_coefficients += stbir_info->vertical.coefficient_width; + } + + // now evict the scanlines that are left over in the ring buffer + while ( split_info->ring_buffer_first_scanline < end_output_y ) + handle_scanline_for_scatter(stbir_info, split_info); + + // update the end_input_y if we do multiple resizes with the same data + ++last_input_y; + for( y = 0 ; y < split_count; y++ ) + if ( split_info[y].end_input_y > last_input_y ) + split_info[y].end_input_y = last_input_y; +} + + +static stbir__kernel_callback * stbir__builtin_kernels[] = { 0, stbir__filter_trapezoid, stbir__filter_triangle, stbir__filter_cubic, stbir__filter_catmullrom, stbir__filter_mitchell, stbir__filter_point }; +static stbir__support_callback * stbir__builtin_supports[] = { 0, stbir__support_trapezoid, stbir__support_one, stbir__support_two, stbir__support_two, stbir__support_two, stbir__support_zeropoint5 }; + +static void stbir__set_sampler(stbir__sampler * samp, stbir_filter filter, stbir__kernel_callback * kernel, stbir__support_callback * support, stbir_edge edge, stbir__scale_info * scale_info, int always_gather, void * user_data ) +{ + // set filter + if (filter == 0) + { + filter = STBIR_DEFAULT_FILTER_DOWNSAMPLE; // default to downsample + if (scale_info->scale >= ( 1.0f - stbir__small_float ) ) + { + if ( (scale_info->scale <= ( 1.0f + stbir__small_float ) ) && ( STBIR_CEILF(scale_info->pixel_shift) == scale_info->pixel_shift ) ) + filter = STBIR_FILTER_POINT_SAMPLE; + else + filter = STBIR_DEFAULT_FILTER_UPSAMPLE; + } + } + samp->filter_enum = filter; + + STBIR_ASSERT(samp->filter_enum != 0); + STBIR_ASSERT((unsigned)samp->filter_enum < STBIR_FILTER_OTHER); + samp->filter_kernel = stbir__builtin_kernels[ filter ]; + samp->filter_support = stbir__builtin_supports[ filter ]; + + if ( kernel && support ) + { + samp->filter_kernel = kernel; + samp->filter_support = support; + samp->filter_enum = STBIR_FILTER_OTHER; + } + + samp->edge = edge; + samp->filter_pixel_width = stbir__get_filter_pixel_width (samp->filter_support, scale_info->scale, user_data ); + // Gather is always better, but in extreme downsamples, you have to most or all of the data in memory + // For horizontal, we always have all the pixels, so we always use gather here (always_gather==1). + // For vertical, we use gather if scaling up (which means we will have samp->filter_pixel_width + // scanlines in memory at once). + samp->is_gather = 0; + if ( scale_info->scale >= ( 1.0f - stbir__small_float ) ) + samp->is_gather = 1; + else if ( ( always_gather ) || ( samp->filter_pixel_width <= STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT ) ) + samp->is_gather = 2; + + // pre calculate stuff based on the above + samp->coefficient_width = stbir__get_coefficient_width(samp, samp->is_gather, user_data); + + if ( edge == STBIR_EDGE_WRAP ) + if ( samp->filter_pixel_width > ( scale_info->input_full_size * 2 ) ) // this can only happen when shrinking to a single pixel + samp->filter_pixel_width = scale_info->input_full_size * 2; + + // This is how much to expand buffers to account for filters seeking outside + // the image boundaries. + samp->filter_pixel_margin = samp->filter_pixel_width / 2; + + samp->num_contributors = stbir__get_contributors(samp, samp->is_gather); + samp->contributors_size = samp->num_contributors * sizeof(stbir__contributors); + samp->coefficients_size = samp->num_contributors * samp->coefficient_width * sizeof(float) + sizeof(float); // extra sizeof(float) is padding + + samp->gather_prescatter_contributors = 0; + samp->gather_prescatter_coefficients = 0; + if ( samp->is_gather == 0 ) + { + samp->gather_prescatter_coefficient_width = samp->filter_pixel_width; + samp->gather_prescatter_num_contributors = stbir__get_contributors(samp, 2); + samp->gather_prescatter_contributors_size = samp->gather_prescatter_num_contributors * sizeof(stbir__contributors); + samp->gather_prescatter_coefficients_size = samp->gather_prescatter_num_contributors * samp->gather_prescatter_coefficient_width * sizeof(float); + } +} + +static void stbir__get_conservative_extents( stbir__sampler * samp, stbir__contributors * range, void * user_data ) +{ + float scale = samp->scale_info.scale; + float out_shift = samp->scale_info.pixel_shift; + stbir__support_callback * support = samp->filter_support; + int input_full_size = samp->scale_info.input_full_size; + stbir_edge edge = samp->edge; + float inv_scale = samp->scale_info.inv_scale; + + STBIR_ASSERT( samp->is_gather != 0 ); + + if ( samp->is_gather == 1 ) + { + int in_first_pixel, in_last_pixel; + float out_filter_radius = support(inv_scale, user_data) * scale; + + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0.5, out_filter_radius, inv_scale, out_shift, input_full_size, edge ); + range->n0 = in_first_pixel; + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, ( (float)(samp->scale_info.output_sub_size-1) ) + 0.5f, out_filter_radius, inv_scale, out_shift, input_full_size, edge ); + range->n1 = in_last_pixel; + } + else if ( samp->is_gather == 2 ) // downsample gather, refine + { + float in_pixels_radius = support(scale, user_data) * inv_scale; + int filter_pixel_margin = samp->filter_pixel_margin; + int output_sub_size = samp->scale_info.output_sub_size; + int input_end; + int n; + int in_first_pixel, in_last_pixel; + + // get a conservative area of the input range + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0, 0, inv_scale, out_shift, input_full_size, edge ); + range->n0 = in_first_pixel; + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, (float)output_sub_size, 0, inv_scale, out_shift, input_full_size, edge ); + range->n1 = in_last_pixel; + + // now go through the margin to the start of area to find bottom + n = range->n0 + 1; + input_end = -filter_pixel_margin; + while( n >= input_end ) + { + int out_first_pixel, out_last_pixel; + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size ); + if ( out_first_pixel > out_last_pixel ) + break; + + if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) ) + range->n0 = n; + --n; + } + + // now go through the end of the area through the margin to find top + n = range->n1 - 1; + input_end = n + 1 + filter_pixel_margin; + while( n <= input_end ) + { + int out_first_pixel, out_last_pixel; + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size ); + if ( out_first_pixel > out_last_pixel ) + break; + if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) ) + range->n1 = n; + ++n; + } + } + + if ( samp->edge == STBIR_EDGE_WRAP ) + { + // if we are wrapping, and we are very close to the image size (so the edges might merge), just use the scanline up to the edge + if ( ( range->n0 > 0 ) && ( range->n1 >= input_full_size ) ) + { + int marg = range->n1 - input_full_size + 1; + if ( ( marg + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= range->n0 ) + range->n0 = 0; + } + if ( ( range->n0 < 0 ) && ( range->n1 < (input_full_size-1) ) ) + { + int marg = -range->n0; + if ( ( input_full_size - marg - STBIR__MERGE_RUNS_PIXEL_THRESHOLD - 1 ) <= range->n1 ) + range->n1 = input_full_size - 1; + } + } + else + { + // for non-edge-wrap modes, we never read over the edge, so clamp + if ( range->n0 < 0 ) + range->n0 = 0; + if ( range->n1 >= input_full_size ) + range->n1 = input_full_size - 1; + } +} + +static void stbir__get_split_info( stbir__per_split_info* split_info, int splits, int output_height, int vertical_pixel_margin, int input_full_height ) +{ + int i, cur; + int left = output_height; + + cur = 0; + for( i = 0 ; i < splits ; i++ ) + { + int each; + split_info[i].start_output_y = cur; + each = left / ( splits - i ); + split_info[i].end_output_y = cur + each; + cur += each; + left -= each; + + // scatter range (updated to minimum as you run it) + split_info[i].start_input_y = -vertical_pixel_margin; + split_info[i].end_input_y = input_full_height + vertical_pixel_margin; + } +} + +static void stbir__free_internal_mem( stbir__info *info ) +{ + #define STBIR__FREE_AND_CLEAR( ptr ) { if ( ptr ) { void * p = (ptr); (ptr) = 0; STBIR_FREE( p, info->user_data); } } + + if ( info ) + { + #ifndef STBIR__SEPARATE_ALLOCATIONS + STBIR__FREE_AND_CLEAR( info->alloced_mem ); + #else + int i,j; + + if ( ( info->vertical.gather_prescatter_contributors ) && ( (void*)info->vertical.gather_prescatter_contributors != (void*)info->split_info[0].decode_buffer ) ) + { + STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_coefficients ); + STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_contributors ); + } + for( i = 0 ; i < info->splits ; i++ ) + { + for( j = 0 ; j < info->alloc_ring_buffer_num_entries ; j++ ) + { + #ifdef STBIR_SIMD8 + if ( info->effective_channels == 3 ) + --info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer + #endif + STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers[j] ); + } + + #ifdef STBIR_SIMD8 + if ( info->effective_channels == 3 ) + --info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer + #endif + STBIR__FREE_AND_CLEAR( info->split_info[i].decode_buffer ); + STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers ); + STBIR__FREE_AND_CLEAR( info->split_info[i].vertical_buffer ); + } + STBIR__FREE_AND_CLEAR( info->split_info ); + if ( info->vertical.coefficients != info->horizontal.coefficients ) + { + STBIR__FREE_AND_CLEAR( info->vertical.coefficients ); + STBIR__FREE_AND_CLEAR( info->vertical.contributors ); + } + STBIR__FREE_AND_CLEAR( info->horizontal.coefficients ); + STBIR__FREE_AND_CLEAR( info->horizontal.contributors ); + STBIR__FREE_AND_CLEAR( info->alloced_mem ); + STBIR__FREE_AND_CLEAR( info ); + #endif + } + + #undef STBIR__FREE_AND_CLEAR +} + +static int stbir__get_max_split( int splits, int height ) +{ + int i; + int max = 0; + + for( i = 0 ; i < splits ; i++ ) + { + int each = height / ( splits - i ); + if ( each > max ) + max = each; + height -= each; + } + return max; +} + +static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_n_coeffs_funcs[8] = +{ + 0, stbir__horizontal_gather_1_channels_with_n_coeffs_funcs, stbir__horizontal_gather_2_channels_with_n_coeffs_funcs, stbir__horizontal_gather_3_channels_with_n_coeffs_funcs, stbir__horizontal_gather_4_channels_with_n_coeffs_funcs, 0,0, stbir__horizontal_gather_7_channels_with_n_coeffs_funcs +}; + +static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_channels_funcs[8] = +{ + 0, stbir__horizontal_gather_1_channels_funcs, stbir__horizontal_gather_2_channels_funcs, stbir__horizontal_gather_3_channels_funcs, stbir__horizontal_gather_4_channels_funcs, 0,0, stbir__horizontal_gather_7_channels_funcs +}; + +// there are six resize classifications: 0 == vertical scatter, 1 == vertical gather < 1x scale, 2 == vertical gather 1x-2x scale, 4 == vertical gather < 3x scale, 4 == vertical gather > 3x scale, 5 == <=4 pixel height, 6 == <=4 pixel wide column +#define STBIR_RESIZE_CLASSIFICATIONS 8 + +static float stbir__compute_weights[5][STBIR_RESIZE_CLASSIFICATIONS][4]= // 5 = 0=1chan, 1=2chan, 2=3chan, 3=4chan, 4=7chan +{ + { + { 1.00000f, 1.00000f, 0.31250f, 1.00000f }, + { 0.56250f, 0.59375f, 0.00000f, 0.96875f }, + { 1.00000f, 0.06250f, 0.00000f, 1.00000f }, + { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.03125f }, + }, { + { 0.00000f, 0.84375f, 0.00000f, 0.03125f }, + { 0.09375f, 0.93750f, 0.00000f, 0.78125f }, + { 0.87500f, 0.21875f, 0.00000f, 0.96875f }, + { 0.09375f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.53125f }, + }, { + { 0.00000f, 0.53125f, 0.00000f, 0.03125f }, + { 0.06250f, 0.96875f, 0.00000f, 0.53125f }, + { 0.87500f, 0.18750f, 0.00000f, 0.93750f }, + { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.56250f }, + }, { + { 0.00000f, 0.50000f, 0.00000f, 0.71875f }, + { 0.06250f, 0.84375f, 0.00000f, 0.87500f }, + { 1.00000f, 0.50000f, 0.50000f, 0.96875f }, + { 1.00000f, 0.09375f, 0.31250f, 0.50000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 1.00000f, 0.03125f, 0.03125f, 0.53125f }, + { 0.18750f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.03125f, 0.18750f }, + }, { + { 0.00000f, 0.59375f, 0.00000f, 0.96875f }, + { 0.06250f, 0.81250f, 0.06250f, 0.59375f }, + { 0.75000f, 0.43750f, 0.12500f, 0.96875f }, + { 0.87500f, 0.06250f, 0.18750f, 0.43750f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.15625f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.03125f, 0.34375f }, + } +}; + +// structure that allow us to query and override info for training the costs +typedef struct STBIR__V_FIRST_INFO +{ + double v_cost, h_cost; + int control_v_first; // 0 = no control, 1 = force hori, 2 = force vert + int v_first; + int v_resize_classification; + int is_gather; +} STBIR__V_FIRST_INFO; + +#ifdef STBIR__V_FIRST_INFO_BUFFER +static STBIR__V_FIRST_INFO STBIR__V_FIRST_INFO_BUFFER = {0}; +#define STBIR__V_FIRST_INFO_POINTER &STBIR__V_FIRST_INFO_BUFFER +#else +#define STBIR__V_FIRST_INFO_POINTER 0 +#endif + +// Figure out whether to scale along the horizontal or vertical first. +// This only *super* important when you are scaling by a massively +// different amount in the vertical vs the horizontal (for example, if +// you are scaling by 2x in the width, and 0.5x in the height, then you +// want to do the vertical scale first, because it's around 3x faster +// in that order. +// +// In more normal circumstances, this makes a 20-40% differences, so +// it's good to get right, but not critical. The normal way that you +// decide which direction goes first is just figuring out which +// direction does more multiplies. But with modern CPUs with their +// fancy caches and SIMD and high IPC abilities, so there's just a lot +// more that goes into it. +// +// My handwavy sort of solution is to have an app that does a whole +// bunch of timing for both vertical and horizontal first modes, +// and then another app that can read lots of these timing files +// and try to search for the best weights to use. Dotimings.c +// is the app that does a bunch of timings, and vf_train.c is the +// app that solves for the best weights (and shows how well it +// does currently). + +static int stbir__should_do_vertical_first( float weights_table[STBIR_RESIZE_CLASSIFICATIONS][4], int horizontal_filter_pixel_width, float horizontal_scale, int horizontal_output_size, int vertical_filter_pixel_width, float vertical_scale, int vertical_output_size, int is_gather, STBIR__V_FIRST_INFO * info ) +{ + double v_cost, h_cost; + float * weights; + int vertical_first; + int v_classification; + + // categorize the resize into buckets + if ( ( vertical_output_size <= 4 ) || ( horizontal_output_size <= 4 ) ) + v_classification = ( vertical_output_size < horizontal_output_size ) ? 6 : 7; + else if ( vertical_scale <= 1.0f ) + v_classification = ( is_gather ) ? 1 : 0; + else if ( vertical_scale <= 2.0f) + v_classification = 2; + else if ( vertical_scale <= 3.0f) + v_classification = 3; + else if ( vertical_scale <= 4.0f) + v_classification = 5; + else + v_classification = 6; + + // use the right weights + weights = weights_table[ v_classification ]; + + // this is the costs when you don't take into account modern CPUs with high ipc and simd and caches - wish we had a better estimate + h_cost = (float)horizontal_filter_pixel_width * weights[0] + horizontal_scale * (float)vertical_filter_pixel_width * weights[1]; + v_cost = (float)vertical_filter_pixel_width * weights[2] + vertical_scale * (float)horizontal_filter_pixel_width * weights[3]; + + // use computation estimate to decide vertical first or not + vertical_first = ( v_cost <= h_cost ) ? 1 : 0; + + // save these, if requested + if ( info ) + { + info->h_cost = h_cost; + info->v_cost = v_cost; + info->v_resize_classification = v_classification; + info->v_first = vertical_first; + info->is_gather = is_gather; + } + + // and this allows us to override everything for testing (see dotiming.c) + if ( ( info ) && ( info->control_v_first ) ) + vertical_first = ( info->control_v_first == 2 ) ? 1 : 0; + + return vertical_first; +} + +// layout lookups - must match stbir_internal_pixel_layout +static unsigned char stbir__pixel_channels[] = { + 1,2,3,3,4, // 1ch, 2ch, rgb, bgr, 4ch + 4,4,4,4,2,2, // RGBA,BGRA,ARGB,ABGR,RA,AR + 4,4,4,4,2,2, // RGBA_PM,BGRA_PM,ARGB_PM,ABGR_PM,RA_PM,AR_PM +}; + +// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types +// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible +static stbir_internal_pixel_layout stbir__pixel_layout_convert_public_to_internal[] = { + STBIRI_BGR, STBIRI_1CHANNEL, STBIRI_2CHANNEL, STBIRI_RGB, STBIRI_RGBA, + STBIRI_4CHANNEL, STBIRI_BGRA, STBIRI_ARGB, STBIRI_ABGR, STBIRI_RA, STBIRI_AR, + STBIRI_RGBA_PM, STBIRI_BGRA_PM, STBIRI_ARGB_PM, STBIRI_ABGR_PM, STBIRI_RA_PM, STBIRI_AR_PM, +}; + +static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sampler * horizontal, stbir__sampler * vertical, stbir__contributors * conservative, stbir_pixel_layout input_pixel_layout_public, stbir_pixel_layout output_pixel_layout_public, int splits, int new_x, int new_y, int fast_alpha, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO ) +{ + static char stbir_channel_count_index[8]={ 9,0,1,2, 3,9,9,4 }; + + stbir__info * info = 0; + void * alloced = 0; + int alloced_total = 0; + int vertical_first; + int decode_buffer_size, ring_buffer_length_bytes, ring_buffer_size, vertical_buffer_size, alloc_ring_buffer_num_entries; + + int alpha_weighting_type = 0; // 0=none, 1=simple, 2=fancy + int conservative_split_output_size = stbir__get_max_split( splits, vertical->scale_info.output_sub_size ); + stbir_internal_pixel_layout input_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ input_pixel_layout_public ]; + stbir_internal_pixel_layout output_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ output_pixel_layout_public ]; + int channels = stbir__pixel_channels[ input_pixel_layout ]; + int effective_channels = channels; + + // first figure out what type of alpha weighting to use (if any) + if ( ( horizontal->filter_enum != STBIR_FILTER_POINT_SAMPLE ) || ( vertical->filter_enum != STBIR_FILTER_POINT_SAMPLE ) ) // no alpha weighting on point sampling + { + if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) ) + { + if ( fast_alpha ) + { + alpha_weighting_type = 4; + } + else + { + static int fancy_alpha_effective_cnts[6] = { 7, 7, 7, 7, 3, 3 }; + alpha_weighting_type = 2; + effective_channels = fancy_alpha_effective_cnts[ input_pixel_layout - STBIRI_RGBA ]; + } + } + else if ( ( input_pixel_layout >= STBIRI_RGBA_PM ) && ( input_pixel_layout <= STBIRI_AR_PM ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) ) + { + // input premult, output non-premult + alpha_weighting_type = 3; + } + else if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA_PM ) && ( output_pixel_layout <= STBIRI_AR_PM ) ) + { + // input non-premult, output premult + alpha_weighting_type = 1; + } + } + + // channel in and out count must match currently + if ( channels != stbir__pixel_channels[ output_pixel_layout ] ) + return 0; + + // get vertical first + vertical_first = stbir__should_do_vertical_first( stbir__compute_weights[ (int)stbir_channel_count_index[ effective_channels ] ], horizontal->filter_pixel_width, horizontal->scale_info.scale, horizontal->scale_info.output_sub_size, vertical->filter_pixel_width, vertical->scale_info.scale, vertical->scale_info.output_sub_size, vertical->is_gather, STBIR__V_FIRST_INFO_POINTER ); + + // sometimes read one float off in some of the unrolled loops (with a weight of zero coeff, so it doesn't have an effect) + decode_buffer_size = ( conservative->n1 - conservative->n0 + 1 ) * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + +#if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8) + if ( effective_channels == 3 ) + decode_buffer_size += sizeof(float); // avx in 3 channel mode needs one float at the start of the buffer (only with separate allocations) +#endif + + ring_buffer_length_bytes = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + + // if we do vertical first, the ring buffer holds a whole decoded line + if ( vertical_first ) + ring_buffer_length_bytes = ( decode_buffer_size + 15 ) & ~15; + + if ( ( ring_buffer_length_bytes & 4095 ) == 0 ) ring_buffer_length_bytes += 64*3; // avoid 4k alias + + // One extra entry because floating point precision problems sometimes cause an extra to be necessary. + alloc_ring_buffer_num_entries = vertical->filter_pixel_width + 1; + + // we never need more ring buffer entries than the scanlines we're outputting when in scatter mode + if ( ( !vertical->is_gather ) && ( alloc_ring_buffer_num_entries > conservative_split_output_size ) ) + alloc_ring_buffer_num_entries = conservative_split_output_size; + + ring_buffer_size = alloc_ring_buffer_num_entries * ring_buffer_length_bytes; + + // The vertical buffer is used differently, depending on whether we are scattering + // the vertical scanlines, or gathering them. + // If scattering, it's used at the temp buffer to accumulate each output. + // If gathering, it's just the output buffer. + vertical_buffer_size = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + + // we make two passes through this loop, 1st to add everything up, 2nd to allocate and init + for(;;) + { + int i; + void * advance_mem = alloced; + int copy_horizontal = 0; + stbir__sampler * possibly_use_horizontal_for_pivot = 0; + +#ifdef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__NEXT_PTR( ptr, size, ntype ) if ( alloced ) { void * p = STBIR_MALLOC( size, user_data); if ( p == 0 ) { stbir__free_internal_mem( info ); return 0; } (ptr) = (ntype*)p; } +#else + #define STBIR__NEXT_PTR( ptr, size, ntype ) advance_mem = (void*) ( ( ((size_t)advance_mem) + 15 ) & ~15 ); if ( alloced ) ptr = (ntype*)advance_mem; advance_mem = ((char*)advance_mem) + (size); +#endif + + STBIR__NEXT_PTR( info, sizeof( stbir__info ), stbir__info ); + + STBIR__NEXT_PTR( info->split_info, sizeof( stbir__per_split_info ) * splits, stbir__per_split_info ); + + if ( info ) + { + static stbir__alpha_weight_func * fancy_alpha_weights[6] = { stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_2ch, stbir__fancy_alpha_weight_2ch }; + static stbir__alpha_unweight_func * fancy_alpha_unweights[6] = { stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_2ch, stbir__fancy_alpha_unweight_2ch }; + static stbir__alpha_weight_func * simple_alpha_weights[6] = { stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_2ch, stbir__simple_alpha_weight_2ch }; + static stbir__alpha_unweight_func * simple_alpha_unweights[6] = { stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_2ch, stbir__simple_alpha_unweight_2ch }; + + // initialize info fields + info->alloced_mem = alloced; + info->alloced_total = alloced_total; + + info->channels = channels; + info->effective_channels = effective_channels; + + info->offset_x = new_x; + info->offset_y = new_y; + info->alloc_ring_buffer_num_entries = alloc_ring_buffer_num_entries; + info->ring_buffer_num_entries = 0; + info->ring_buffer_length_bytes = ring_buffer_length_bytes; + info->splits = splits; + info->vertical_first = vertical_first; + + info->input_pixel_layout_internal = input_pixel_layout; + info->output_pixel_layout_internal = output_pixel_layout; + + // setup alpha weight functions + info->alpha_weight = 0; + info->alpha_unweight = 0; + + // handle alpha weighting functions and overrides + if ( alpha_weighting_type == 2 ) + { + // high quality alpha multiplying on the way in, dividing on the way out + info->alpha_weight = fancy_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + info->alpha_unweight = fancy_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 4 ) + { + // fast alpha multiplying on the way in, dividing on the way out + info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 1 ) + { + // fast alpha on the way in, leave in premultiplied form on way out + info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 3 ) + { + // incoming is premultiplied, fast alpha dividing on the way out - non-premultiplied output + info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + + // handle 3-chan color flipping, using the alpha weight path + if ( ( ( input_pixel_layout == STBIRI_RGB ) && ( output_pixel_layout == STBIRI_BGR ) ) || + ( ( input_pixel_layout == STBIRI_BGR ) && ( output_pixel_layout == STBIRI_RGB ) ) ) + { + // do the flipping on the smaller of the two ends + if ( horizontal->scale_info.scale < 1.0f ) + info->alpha_unweight = stbir__simple_flip_3ch; + else + info->alpha_weight = stbir__simple_flip_3ch; + } + + } + + // get all the per-split buffers + for( i = 0 ; i < splits ; i++ ) + { + STBIR__NEXT_PTR( info->split_info[i].decode_buffer, decode_buffer_size, float ); + +#ifdef STBIR__SEPARATE_ALLOCATIONS + + #ifdef STBIR_SIMD8 + if ( ( info ) && ( effective_channels == 3 ) ) + ++info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer + #endif + + STBIR__NEXT_PTR( info->split_info[i].ring_buffers, alloc_ring_buffer_num_entries * sizeof(float*), float* ); + { + int j; + for( j = 0 ; j < alloc_ring_buffer_num_entries ; j++ ) + { + STBIR__NEXT_PTR( info->split_info[i].ring_buffers[j], ring_buffer_length_bytes, float ); + #ifdef STBIR_SIMD8 + if ( ( info ) && ( effective_channels == 3 ) ) + ++info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer + #endif + } + } +#else + STBIR__NEXT_PTR( info->split_info[i].ring_buffer, ring_buffer_size, float ); +#endif + STBIR__NEXT_PTR( info->split_info[i].vertical_buffer, vertical_buffer_size, float ); + } + + // alloc memory for to-be-pivoted coeffs (if necessary) + if ( vertical->is_gather == 0 ) + { + int both; + int temp_mem_amt; + + // when in vertical scatter mode, we first build the coefficients in gather mode, and then pivot after, + // that means we need two buffers, so we try to use the decode buffer and ring buffer for this. if that + // is too small, we just allocate extra memory to use as this temp. + + both = vertical->gather_prescatter_contributors_size + vertical->gather_prescatter_coefficients_size; + +#ifdef STBIR__SEPARATE_ALLOCATIONS + temp_mem_amt = decode_buffer_size; +#else + temp_mem_amt = ( decode_buffer_size + ring_buffer_size + vertical_buffer_size ) * splits; +#endif + if ( temp_mem_amt >= both ) + { + if ( info ) + { + vertical->gather_prescatter_contributors = (stbir__contributors*)info->split_info[0].decode_buffer; + vertical->gather_prescatter_coefficients = (float*) ( ( (char*)info->split_info[0].decode_buffer ) + vertical->gather_prescatter_contributors_size ); + } + } + else + { + // ring+decode memory is too small, so allocate temp memory + STBIR__NEXT_PTR( vertical->gather_prescatter_contributors, vertical->gather_prescatter_contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( vertical->gather_prescatter_coefficients, vertical->gather_prescatter_coefficients_size, float ); + } + } + + STBIR__NEXT_PTR( horizontal->contributors, horizontal->contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( horizontal->coefficients, horizontal->coefficients_size, float ); + + // are the two filters identical?? (happens a lot with mipmap generation) + if ( ( horizontal->filter_kernel == vertical->filter_kernel ) && ( horizontal->filter_support == vertical->filter_support ) && ( horizontal->edge == vertical->edge ) && ( horizontal->scale_info.output_sub_size == vertical->scale_info.output_sub_size ) ) + { + float diff_scale = horizontal->scale_info.scale - vertical->scale_info.scale; + float diff_shift = horizontal->scale_info.pixel_shift - vertical->scale_info.pixel_shift; + if ( diff_scale < 0.0f ) diff_scale = -diff_scale; + if ( diff_shift < 0.0f ) diff_shift = -diff_shift; + if ( ( diff_scale <= stbir__small_float ) && ( diff_shift <= stbir__small_float ) ) + { + if ( horizontal->is_gather == vertical->is_gather ) + { + copy_horizontal = 1; + goto no_vert_alloc; + } + // everything matches, but vertical is scatter, horizontal is gather, use horizontal coeffs for vertical pivot coeffs + possibly_use_horizontal_for_pivot = horizontal; + } + } + + STBIR__NEXT_PTR( vertical->contributors, vertical->contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( vertical->coefficients, vertical->coefficients_size, float ); + + no_vert_alloc: + + if ( info ) + { + STBIR_PROFILE_BUILD_START( horizontal ); + + stbir__calculate_filters( horizontal, 0, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + + // setup the horizontal gather functions + // start with defaulting to the n_coeffs functions (specialized on channels and remnant leftover) + info->horizontal_gather_channels = stbir__horizontal_gather_n_coeffs_funcs[ effective_channels ][ horizontal->extent_info.widest & 3 ]; + // but if the number of coeffs <= 12, use another set of special cases. <=12 coeffs is any enlarging resize, or shrinking resize down to about 1/3 size + if ( horizontal->extent_info.widest <= 12 ) + info->horizontal_gather_channels = stbir__horizontal_gather_channels_funcs[ effective_channels ][ horizontal->extent_info.widest - 1 ]; + + info->scanline_extents.conservative.n0 = conservative->n0; + info->scanline_extents.conservative.n1 = conservative->n1; + + // get exact extents + stbir__get_extents( horizontal, &info->scanline_extents ); + + // pack the horizontal coeffs + horizontal->coefficient_width = stbir__pack_coefficients(horizontal->num_contributors, horizontal->contributors, horizontal->coefficients, horizontal->coefficient_width, horizontal->extent_info.widest, info->scanline_extents.conservative.n1 + 1 ); + + STBIR_MEMCPY( &info->horizontal, horizontal, sizeof( stbir__sampler ) ); + + STBIR_PROFILE_BUILD_END( horizontal ); + + if ( copy_horizontal ) + { + STBIR_MEMCPY( &info->vertical, horizontal, sizeof( stbir__sampler ) ); + } + else + { + STBIR_PROFILE_BUILD_START( vertical ); + + stbir__calculate_filters( vertical, possibly_use_horizontal_for_pivot, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + STBIR_MEMCPY( &info->vertical, vertical, sizeof( stbir__sampler ) ); + + STBIR_PROFILE_BUILD_END( vertical ); + } + + // setup the vertical split ranges + stbir__get_split_info( info->split_info, info->splits, info->vertical.scale_info.output_sub_size, info->vertical.filter_pixel_margin, info->vertical.scale_info.input_full_size ); + + // now we know precisely how many entries we need + info->ring_buffer_num_entries = info->vertical.extent_info.widest; + + // we never need more ring buffer entries than the scanlines we're outputting + if ( ( !info->vertical.is_gather ) && ( info->ring_buffer_num_entries > conservative_split_output_size ) ) + info->ring_buffer_num_entries = conservative_split_output_size; + STBIR_ASSERT( info->ring_buffer_num_entries <= info->alloc_ring_buffer_num_entries ); + + // a few of the horizontal gather functions read one dword past the end (but mask it out), so put in a normal value so no snans or denormals accidentally sneak in + for( i = 0 ; i < splits ; i++ ) + { + int width, ofs; + + // find the right most span + if ( info->scanline_extents.spans[0].n1 > info->scanline_extents.spans[1].n1 ) + width = info->scanline_extents.spans[0].n1 - info->scanline_extents.spans[0].n0; + else + width = info->scanline_extents.spans[1].n1 - info->scanline_extents.spans[1].n0; + + // this calc finds the exact end of the decoded scanline for all filter modes. + // usually this is just the width * effective channels. But we have to account + // for the area to the left of the scanline for wrap filtering and alignment, this + // is stored as a negative value in info->scanline_extents.conservative.n0. Next, + // we need to skip the exact size of the right hand size filter area (again for + // wrap mode), this is in info->scanline_extents.edge_sizes[1]). + ofs = ( width + 1 - info->scanline_extents.conservative.n0 + info->scanline_extents.edge_sizes[1] ) * effective_channels; + + // place a known, but numerically valid value in the decode buffer + info->split_info[i].decode_buffer[ ofs ] = 9999.0f; + + // if vertical filtering first, place a known, but numerically valid value in the all + // of the ring buffer accumulators + if ( vertical_first ) + { + int j; + for( j = 0; j < info->ring_buffer_num_entries ; j++ ) + { + stbir__get_ring_buffer_entry( info, info->split_info + i, j )[ ofs ] = 9999.0f; + } + } + } + } + + #undef STBIR__NEXT_PTR + + + // is this the first time through loop? + if ( info == 0 ) + { + alloced_total = (int) ( 15 + (size_t)advance_mem ); + alloced = STBIR_MALLOC( alloced_total, user_data ); + if ( alloced == 0 ) + return 0; + } + else + return info; // success + } +} + +static int stbir__perform_resize( stbir__info const * info, int split_start, int split_count ) +{ + stbir__per_split_info * split_info = info->split_info + split_start; + + STBIR_PROFILE_CLEAR_EXTRAS(); + + STBIR_PROFILE_FIRST_START( looping ); + if (info->vertical.is_gather) + stbir__vertical_gather_loop( info, split_info, split_count ); + else + stbir__vertical_scatter_loop( info, split_info, split_count ); + STBIR_PROFILE_END( looping ); + + return 1; +} + +static void stbir__update_info_from_resize( stbir__info * info, STBIR_RESIZE * resize ) +{ + static stbir__decode_pixels_func * decode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + /* 1ch-4ch */ stbir__decode_uint8_srgb, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear, + }; + + static stbir__decode_pixels_func * decode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + { /* RGBA */ stbir__decode_uint8_srgb4_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear }, + { /* BGRA */ stbir__decode_uint8_srgb4_linearalpha_BGRA, stbir__decode_uint8_srgb_BGRA, 0, stbir__decode_float_linear_BGRA, stbir__decode_half_float_linear_BGRA }, + { /* ARGB */ stbir__decode_uint8_srgb4_linearalpha_ARGB, stbir__decode_uint8_srgb_ARGB, 0, stbir__decode_float_linear_ARGB, stbir__decode_half_float_linear_ARGB }, + { /* ABGR */ stbir__decode_uint8_srgb4_linearalpha_ABGR, stbir__decode_uint8_srgb_ABGR, 0, stbir__decode_float_linear_ABGR, stbir__decode_half_float_linear_ABGR }, + { /* RA */ stbir__decode_uint8_srgb2_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear }, + { /* AR */ stbir__decode_uint8_srgb2_linearalpha_AR, stbir__decode_uint8_srgb_AR, 0, stbir__decode_float_linear_AR, stbir__decode_half_float_linear_AR }, + }; + + static stbir__decode_pixels_func * decode_simple_scaled_or_not[2][2]= + { + { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear }, + }; + + static stbir__decode_pixels_func * decode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]= + { + { /* RGBA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } }, + { /* BGRA */ { stbir__decode_uint8_linear_scaled_BGRA, stbir__decode_uint8_linear_BGRA }, { stbir__decode_uint16_linear_scaled_BGRA, stbir__decode_uint16_linear_BGRA } }, + { /* ARGB */ { stbir__decode_uint8_linear_scaled_ARGB, stbir__decode_uint8_linear_ARGB }, { stbir__decode_uint16_linear_scaled_ARGB, stbir__decode_uint16_linear_ARGB } }, + { /* ABGR */ { stbir__decode_uint8_linear_scaled_ABGR, stbir__decode_uint8_linear_ABGR }, { stbir__decode_uint16_linear_scaled_ABGR, stbir__decode_uint16_linear_ABGR } }, + { /* RA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } }, + { /* AR */ { stbir__decode_uint8_linear_scaled_AR, stbir__decode_uint8_linear_AR }, { stbir__decode_uint16_linear_scaled_AR, stbir__decode_uint16_linear_AR } } + }; + + static stbir__encode_pixels_func * encode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + /* 1ch-4ch */ stbir__encode_uint8_srgb, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear, + }; + + static stbir__encode_pixels_func * encode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + { /* RGBA */ stbir__encode_uint8_srgb4_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear }, + { /* BGRA */ stbir__encode_uint8_srgb4_linearalpha_BGRA, stbir__encode_uint8_srgb_BGRA, 0, stbir__encode_float_linear_BGRA, stbir__encode_half_float_linear_BGRA }, + { /* ARGB */ stbir__encode_uint8_srgb4_linearalpha_ARGB, stbir__encode_uint8_srgb_ARGB, 0, stbir__encode_float_linear_ARGB, stbir__encode_half_float_linear_ARGB }, + { /* ABGR */ stbir__encode_uint8_srgb4_linearalpha_ABGR, stbir__encode_uint8_srgb_ABGR, 0, stbir__encode_float_linear_ABGR, stbir__encode_half_float_linear_ABGR }, + { /* RA */ stbir__encode_uint8_srgb2_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear }, + { /* AR */ stbir__encode_uint8_srgb2_linearalpha_AR, stbir__encode_uint8_srgb_AR, 0, stbir__encode_float_linear_AR, stbir__encode_half_float_linear_AR } + }; + + static stbir__encode_pixels_func * encode_simple_scaled_or_not[2][2]= + { + { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear }, + }; + + static stbir__encode_pixels_func * encode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]= + { + { /* RGBA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } }, + { /* BGRA */ { stbir__encode_uint8_linear_scaled_BGRA, stbir__encode_uint8_linear_BGRA }, { stbir__encode_uint16_linear_scaled_BGRA, stbir__encode_uint16_linear_BGRA } }, + { /* ARGB */ { stbir__encode_uint8_linear_scaled_ARGB, stbir__encode_uint8_linear_ARGB }, { stbir__encode_uint16_linear_scaled_ARGB, stbir__encode_uint16_linear_ARGB } }, + { /* ABGR */ { stbir__encode_uint8_linear_scaled_ABGR, stbir__encode_uint8_linear_ABGR }, { stbir__encode_uint16_linear_scaled_ABGR, stbir__encode_uint16_linear_ABGR } }, + { /* RA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } }, + { /* AR */ { stbir__encode_uint8_linear_scaled_AR, stbir__encode_uint8_linear_AR }, { stbir__encode_uint16_linear_scaled_AR, stbir__encode_uint16_linear_AR } } + }; + + stbir__decode_pixels_func * decode_pixels = 0; + stbir__encode_pixels_func * encode_pixels = 0; + stbir_datatype input_type, output_type; + + input_type = resize->input_data_type; + output_type = resize->output_data_type; + info->input_data = resize->input_pixels; + info->input_stride_bytes = resize->input_stride_in_bytes; + info->output_stride_bytes = resize->output_stride_in_bytes; + + // if we're completely point sampling, then we can turn off SRGB + if ( ( info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( info->vertical.filter_enum == STBIR_FILTER_POINT_SAMPLE ) ) + { + if ( ( ( input_type == STBIR_TYPE_UINT8_SRGB ) || ( input_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) && + ( ( output_type == STBIR_TYPE_UINT8_SRGB ) || ( output_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) ) + { + input_type = STBIR_TYPE_UINT8; + output_type = STBIR_TYPE_UINT8; + } + } + + // recalc the output and input strides + if ( info->input_stride_bytes == 0 ) + info->input_stride_bytes = info->channels * info->horizontal.scale_info.input_full_size * stbir__type_size[input_type]; + + if ( info->output_stride_bytes == 0 ) + info->output_stride_bytes = info->channels * info->horizontal.scale_info.output_sub_size * stbir__type_size[output_type]; + + // calc offset + info->output_data = ( (char*) resize->output_pixels ) + ( (ptrdiff_t) info->offset_y * (ptrdiff_t) resize->output_stride_in_bytes ) + ( info->offset_x * info->channels * stbir__type_size[output_type] ); + + info->in_pixels_cb = resize->input_cb; + info->user_data = resize->user_data; + info->out_pixels_cb = resize->output_cb; + + // setup the input format converters + if ( ( input_type == STBIR_TYPE_UINT8 ) || ( input_type == STBIR_TYPE_UINT16 ) ) + { + int non_scaled = 0; + + // check if we can run unscaled - 0-255.0/0-65535.0 instead of 0-1.0 (which is a tiny bit faster when doing linear 8->8 or 16->16) + if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual) + if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) ) + non_scaled = 1; + + if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL ) + decode_pixels = decode_simple_scaled_or_not[ input_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + else + decode_pixels = decode_alphas_scaled_or_not[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + } + else + { + if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL ) + decode_pixels = decode_simple[ input_type - STBIR_TYPE_UINT8_SRGB ]; + else + decode_pixels = decode_alphas[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type - STBIR_TYPE_UINT8_SRGB ]; + } + + // setup the output format converters + if ( ( output_type == STBIR_TYPE_UINT8 ) || ( output_type == STBIR_TYPE_UINT16 ) ) + { + int non_scaled = 0; + + // check if we can run unscaled - 0-255.0/0-65535.0 instead of 0-1.0 (which is a tiny bit faster when doing linear 8->8 or 16->16) + if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual) + if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) ) + non_scaled = 1; + + if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL ) + encode_pixels = encode_simple_scaled_or_not[ output_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + else + encode_pixels = encode_alphas_scaled_or_not[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + } + else + { + if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL ) + encode_pixels = encode_simple[ output_type - STBIR_TYPE_UINT8_SRGB ]; + else + encode_pixels = encode_alphas[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type - STBIR_TYPE_UINT8_SRGB ]; + } + + info->input_type = input_type; + info->output_type = output_type; + info->decode_pixels = decode_pixels; + info->encode_pixels = encode_pixels; +} + +static void stbir__clip( int * outx, int * outsubw, int outw, double * u0, double * u1 ) +{ + double per, adj; + int over; + + // do left/top edge + if ( *outx < 0 ) + { + per = ( (double)*outx ) / ( (double)*outsubw ); // is negative + adj = per * ( *u1 - *u0 ); + *u0 -= adj; // increases u0 + *outx = 0; + } + + // do right/bot edge + over = outw - ( *outx + *outsubw ); + if ( over < 0 ) + { + per = ( (double)over ) / ( (double)*outsubw ); // is negative + adj = per * ( *u1 - *u0 ); + *u1 += adj; // decrease u1 + *outsubw = outw - *outx; + } +} + +// converts a double to a rational that has less than one float bit of error (returns 0 if unable to do so) +static int stbir__double_to_rational(double f, stbir_uint32 limit, stbir_uint32 *numer, stbir_uint32 *denom, int limit_denom ) // limit_denom (1) or limit numer (0) +{ + double err; + stbir_uint64 top, bot; + stbir_uint64 numer_last = 0; + stbir_uint64 denom_last = 1; + stbir_uint64 numer_estimate = 1; + stbir_uint64 denom_estimate = 0; + + // scale to past float error range + top = (stbir_uint64)( f * (double)(1 << 25) ); + bot = 1 << 25; + + // keep refining, but usually stops in a few loops - usually 5 for bad cases + for(;;) + { + stbir_uint64 est, temp; + + // hit limit, break out and do best full range estimate + if ( ( ( limit_denom ) ? denom_estimate : numer_estimate ) >= limit ) + break; + + // is the current error less than 1 bit of a float? if so, we're done + if ( denom_estimate ) + { + err = ( (double)numer_estimate / (double)denom_estimate ) - f; + if ( err < 0.0 ) err = -err; + if ( err < ( 1.0 / (double)(1<<24) ) ) + { + // yup, found it + *numer = (stbir_uint32) numer_estimate; + *denom = (stbir_uint32) denom_estimate; + return 1; + } + } + + // no more refinement bits left? break out and do full range estimate + if ( bot == 0 ) + break; + + // gcd the estimate bits + est = top / bot; + temp = top % bot; + top = bot; + bot = temp; + + // move remainders + temp = est * denom_estimate + denom_last; + denom_last = denom_estimate; + denom_estimate = temp; + + // move remainders + temp = est * numer_estimate + numer_last; + numer_last = numer_estimate; + numer_estimate = temp; + } + + // we didn't fine anything good enough for float, use a full range estimate + if ( limit_denom ) + { + numer_estimate= (stbir_uint64)( f * (double)limit + 0.5 ); + denom_estimate = limit; + } + else + { + numer_estimate = limit; + denom_estimate = (stbir_uint64)( ( (double)limit / f ) + 0.5 ); + } + + *numer = (stbir_uint32) numer_estimate; + *denom = (stbir_uint32) denom_estimate; + + err = ( denom_estimate ) ? ( ( (double)(stbir_uint32)numer_estimate / (double)(stbir_uint32)denom_estimate ) - f ) : 1.0; + if ( err < 0.0 ) err = -err; + return ( err < ( 1.0 / (double)(1<<24) ) ) ? 1 : 0; +} + +static int stbir__calculate_region_transform( stbir__scale_info * scale_info, int output_full_range, int * output_offset, int output_sub_range, int input_full_range, double input_s0, double input_s1 ) +{ + double output_range, input_range, output_s, input_s, ratio, scale; + + input_s = input_s1 - input_s0; + + // null area + if ( ( output_full_range == 0 ) || ( input_full_range == 0 ) || + ( output_sub_range == 0 ) || ( input_s <= stbir__small_float ) ) + return 0; + + // are either of the ranges completely out of bounds? + if ( ( *output_offset >= output_full_range ) || ( ( *output_offset + output_sub_range ) <= 0 ) || ( input_s0 >= (1.0f-stbir__small_float) ) || ( input_s1 <= stbir__small_float ) ) + return 0; + + output_range = (double)output_full_range; + input_range = (double)input_full_range; + + output_s = ( (double)output_sub_range) / output_range; + + // figure out the scaling to use + ratio = output_s / input_s; + + // save scale before clipping + scale = ( output_range / input_range ) * ratio; + scale_info->scale = (float)scale; + scale_info->inv_scale = (float)( 1.0 / scale ); + + // clip output area to left/right output edges (and adjust input area) + stbir__clip( output_offset, &output_sub_range, output_full_range, &input_s0, &input_s1 ); + + // recalc input area + input_s = input_s1 - input_s0; + + // after clipping do we have zero input area? + if ( input_s <= stbir__small_float ) + return 0; + + // calculate and store the starting source offsets in output pixel space + scale_info->pixel_shift = (float) ( input_s0 * ratio * output_range ); + + scale_info->scale_is_rational = stbir__double_to_rational( scale, ( scale <= 1.0 ) ? output_full_range : input_full_range, &scale_info->scale_numerator, &scale_info->scale_denominator, ( scale >= 1.0 ) ); + + scale_info->input_full_size = input_full_range; + scale_info->output_sub_size = output_sub_range; + + return 1; +} + + +static void stbir__init_and_set_layout( STBIR_RESIZE * resize, stbir_pixel_layout pixel_layout, stbir_datatype data_type ) +{ + resize->input_cb = 0; + resize->output_cb = 0; + resize->user_data = resize; + resize->samplers = 0; + resize->needs_rebuild = 1; + resize->called_alloc = 0; + resize->horizontal_filter = STBIR_FILTER_DEFAULT; + resize->horizontal_filter_kernel = 0; resize->horizontal_filter_support = 0; + resize->vertical_filter = STBIR_FILTER_DEFAULT; + resize->vertical_filter_kernel = 0; resize->vertical_filter_support = 0; + resize->horizontal_edge = STBIR_EDGE_CLAMP; + resize->vertical_edge = STBIR_EDGE_CLAMP; + resize->input_s0 = 0; resize->input_t0 = 0; resize->input_s1 = 1; resize->input_t1 = 1; + resize->output_subx = 0; resize->output_suby = 0; resize->output_subw = resize->output_w; resize->output_subh = resize->output_h; + resize->input_data_type = data_type; + resize->output_data_type = data_type; + resize->input_pixel_layout_public = pixel_layout; + resize->output_pixel_layout_public = pixel_layout; +} + +STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize, + const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero + stbir_pixel_layout pixel_layout, stbir_datatype data_type ) +{ + resize->input_pixels = input_pixels; + resize->input_w = input_w; + resize->input_h = input_h; + resize->input_stride_in_bytes = input_stride_in_bytes; + resize->output_pixels = output_pixels; + resize->output_w = output_w; + resize->output_h = output_h; + resize->output_stride_in_bytes = output_stride_in_bytes; + resize->fast_alpha = 0; + + stbir__init_and_set_layout( resize, pixel_layout, data_type ); +} + +// You can update parameters any time after resize_init +STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ) // by default, datatype from resize_init +{ + resize->input_data_type = input_type; + resize->output_data_type = output_type; +} + +STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ) // no callbacks by default +{ + resize->input_cb = input_cb; + resize->output_cb = output_cb; +} + +STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ) // pass back STBIR_RESIZE* by default +{ + resize->user_data = user_data; +} + +STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes ) +{ + resize->input_pixels = input_pixels; + resize->input_stride_in_bytes = input_stride_in_bytes; + resize->output_pixels = output_pixels; + resize->output_stride_in_bytes = output_stride_in_bytes; +} + + +STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ) // CLAMP by default +{ + resize->horizontal_edge = horizontal_edge; + resize->vertical_edge = vertical_edge; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ) // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default +{ + resize->horizontal_filter = horizontal_filter; + resize->vertical_filter = vertical_filter; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support ) +{ + resize->horizontal_filter_kernel = horizontal_filter; resize->horizontal_filter_support = horizontal_support; + resize->vertical_filter_kernel = vertical_filter; resize->vertical_filter_support = vertical_support; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ) // sets new pixel layouts +{ + resize->input_pixel_layout_public = input_pixel_layout; + resize->output_pixel_layout_public = output_pixel_layout; + resize->needs_rebuild = 1; + return 1; +} + + +STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ) // sets alpha speed +{ + resize->fast_alpha = non_pma_alpha_speed_over_quality; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ) // sets input region (full region by default) +{ + resize->input_s0 = s0; + resize->input_t0 = t0; + resize->input_s1 = s1; + resize->input_t1 = t1; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( s1 < stbir__small_float ) || ( (s1-s0) < stbir__small_float ) || + ( t1 < stbir__small_float ) || ( (t1-t0) < stbir__small_float ) || + ( s0 > (1.0f-stbir__small_float) ) || + ( t0 > (1.0f-stbir__small_float) ) ) + return 0; + + return 1; +} + +STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets input region (full region by default) +{ + resize->output_subx = subx; + resize->output_suby = suby; + resize->output_subw = subw; + resize->output_subh = subh; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) ) + return 0; + + return 1; +} + +STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets both regions (full regions by default) +{ + double s0, t0, s1, t1; + + s0 = ( (double)subx ) / ( (double)resize->output_w ); + t0 = ( (double)suby ) / ( (double)resize->output_h ); + s1 = ( (double)(subx+subw) ) / ( (double)resize->output_w ); + t1 = ( (double)(suby+subh) ) / ( (double)resize->output_h ); + + resize->input_s0 = s0; + resize->input_t0 = t0; + resize->input_s1 = s1; + resize->input_t1 = t1; + resize->output_subx = subx; + resize->output_suby = suby; + resize->output_subw = subw; + resize->output_subh = subh; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) ) + return 0; + + return 1; +} + +static int stbir__perform_build( STBIR_RESIZE * resize, int splits ) +{ + stbir__contributors conservative = { 0, 0 }; + stbir__sampler horizontal, vertical; + int new_output_subx, new_output_suby; + stbir__info * out_info; + #ifdef STBIR_PROFILE + stbir__info profile_infod; // used to contain building profile info before everything is allocated + stbir__info * profile_info = &profile_infod; + #endif + + // have we already built the samplers? + if ( resize->samplers ) + return 0; + + #define STBIR_RETURN_ERROR_AND_ASSERT( exp ) STBIR_ASSERT( !(exp) ); if (exp) return 0; + STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->horizontal_filter >= STBIR_FILTER_OTHER) + STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->vertical_filter >= STBIR_FILTER_OTHER) + #undef STBIR_RETURN_ERROR_AND_ASSERT + + if ( splits <= 0 ) + return 0; + + STBIR_PROFILE_BUILD_FIRST_START( build ); + + new_output_subx = resize->output_subx; + new_output_suby = resize->output_suby; + + // do horizontal clip and scale calcs + if ( !stbir__calculate_region_transform( &horizontal.scale_info, resize->output_w, &new_output_subx, resize->output_subw, resize->input_w, resize->input_s0, resize->input_s1 ) ) + return 0; + + // do vertical clip and scale calcs + if ( !stbir__calculate_region_transform( &vertical.scale_info, resize->output_h, &new_output_suby, resize->output_subh, resize->input_h, resize->input_t0, resize->input_t1 ) ) + return 0; + + // if nothing to do, just return + if ( ( horizontal.scale_info.output_sub_size == 0 ) || ( vertical.scale_info.output_sub_size == 0 ) ) + return 0; + + stbir__set_sampler(&horizontal, resize->horizontal_filter, resize->horizontal_filter_kernel, resize->horizontal_filter_support, resize->horizontal_edge, &horizontal.scale_info, 1, resize->user_data ); + stbir__get_conservative_extents( &horizontal, &conservative, resize->user_data ); + stbir__set_sampler(&vertical, resize->vertical_filter, resize->horizontal_filter_kernel, resize->vertical_filter_support, resize->vertical_edge, &vertical.scale_info, 0, resize->user_data ); + + if ( ( vertical.scale_info.output_sub_size / splits ) < 4 ) // each split should be a minimum of 4 scanlines (handwavey choice) + { + splits = vertical.scale_info.output_sub_size / 4; + if ( splits == 0 ) splits = 1; + } + + STBIR_PROFILE_BUILD_START( alloc ); + out_info = stbir__alloc_internal_mem_and_build_samplers( &horizontal, &vertical, &conservative, resize->input_pixel_layout_public, resize->output_pixel_layout_public, splits, new_output_subx, new_output_suby, resize->fast_alpha, resize->user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + STBIR_PROFILE_BUILD_END( alloc ); + STBIR_PROFILE_BUILD_END( build ); + + if ( out_info ) + { + resize->splits = splits; + resize->samplers = out_info; + resize->needs_rebuild = 0; + #ifdef STBIR_PROFILE + STBIR_MEMCPY( &out_info->profile, &profile_infod.profile, sizeof( out_info->profile ) ); + #endif + return splits; + } + + return 0; +} + +void stbir_free_samplers( STBIR_RESIZE * resize ) +{ + if ( resize->samplers ) + { + stbir__free_internal_mem( resize->samplers ); + resize->samplers = 0; + resize->called_alloc = 0; + } +} + +STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int splits ) +{ + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + { + if ( resize->samplers ) + stbir_free_samplers( resize ); + + resize->called_alloc = 1; + return stbir__perform_build( resize, splits ); + } + + STBIR_PROFILE_BUILD_CLEAR( resize->samplers ); + + return 1; +} + +STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize ) +{ + return stbir_build_samplers_with_splits( resize, 1 ); +} + +STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize ) +{ + int result; + + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + { + int alloc_state = resize->called_alloc; // remember allocated state + + if ( resize->samplers ) + { + stbir__free_internal_mem( resize->samplers ); + resize->samplers = 0; + } + + if ( !stbir_build_samplers( resize ) ) + return 0; + + resize->called_alloc = alloc_state; + + // if build_samplers succeeded (above), but there are no samplers set, then + // the area to stretch into was zero pixels, so don't do anything and return + // success + if ( resize->samplers == 0 ) + return 1; + } + else + { + // didn't build anything - clear it + STBIR_PROFILE_BUILD_CLEAR( resize->samplers ); + } + + + // update anything that can be changed without recalcing samplers + stbir__update_info_from_resize( resize->samplers, resize ); + + // do resize + result = stbir__perform_resize( resize->samplers, 0, resize->splits ); + + // if we alloced, then free + if ( !resize->called_alloc ) + { + stbir_free_samplers( resize ); + resize->samplers = 0; + } + + return result; +} + +STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count ) +{ + STBIR_ASSERT( resize->samplers ); + + // if we're just doing the whole thing, call full + if ( ( split_start == -1 ) || ( ( split_start == 0 ) && ( split_count == resize->splits ) ) ) + return stbir_resize_extended( resize ); + + // you **must** build samplers first when using split resize + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + return 0; + + if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) ) + return 0; + + // update anything that can be changed without recalcing samplers + stbir__update_info_from_resize( resize->samplers, resize ); + + // do resize + return stbir__perform_resize( resize->samplers, split_start, split_count ); +} + +static int stbir__check_output_stuff( void ** ret_ptr, int * ret_pitch, void * output_pixels, int type_size, int output_w, int output_h, int output_stride_in_bytes, stbir_internal_pixel_layout pixel_layout ) +{ + size_t size; + int pitch; + void * ptr; + + pitch = output_w * type_size * stbir__pixel_channels[ pixel_layout ]; + if ( pitch == 0 ) + return 0; + + if ( output_stride_in_bytes == 0 ) + output_stride_in_bytes = pitch; + + if ( output_stride_in_bytes < pitch ) + return 0; + + size = output_stride_in_bytes * output_h; + if ( size == 0 ) + return 0; + + *ret_ptr = 0; + *ret_pitch = output_stride_in_bytes; + + if ( output_pixels == 0 ) + { + ptr = STBIR_MALLOC( size, 0 ); + if ( ptr == 0 ) + return 0; + + *ret_ptr = ptr; + *ret_pitch = pitch; + } + + return 1; +} + + +STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + unsigned char * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_UINT8 ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + +STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + unsigned char * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_UINT8_SRGB ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + + +STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + float * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( float ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_FLOAT ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + + +STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, + stbir_edge edge, stbir_filter filter ) +{ + STBIR_RESIZE resize; + float * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, stbir__type_size[data_type], output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout, data_type ); + + resize.horizontal_edge = edge; + resize.vertical_edge = edge; + resize.horizontal_filter = filter; + resize.vertical_filter = filter; + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + +#ifdef STBIR_PROFILE + +STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize ) +{ + static char const * bdescriptions[6] = { "Building", "Allocating", "Horizontal sampler", "Vertical sampler", "Coefficient cleanup", "Coefficient piovot" } ; + stbir__info* samp = resize->samplers; + int i; + + typedef int testa[ (STBIR__ARRAY_SIZE( bdescriptions ) == (STBIR__ARRAY_SIZE( samp->profile.array )-1) )?1:-1]; + typedef int testb[ (sizeof( samp->profile.array ) == (sizeof(samp->profile.named)) )?1:-1]; + typedef int testc[ (sizeof( info->clocks ) >= (sizeof(samp->profile.named)) )?1:-1]; + + for( i = 0 ; i < STBIR__ARRAY_SIZE( bdescriptions ) ; i++) + info->clocks[i] = samp->profile.array[i+1]; + + info->total_clocks = samp->profile.named.total; + info->descriptions = bdescriptions; + info->count = STBIR__ARRAY_SIZE( bdescriptions ); +} + +STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize, int split_start, int split_count ) +{ + static char const * descriptions[7] = { "Looping", "Vertical sampling", "Horizontal sampling", "Scanline input", "Scanline output", "Alpha weighting", "Alpha unweighting" }; + stbir__per_split_info * split_info; + int s, i; + + typedef int testa[ (STBIR__ARRAY_SIZE( descriptions ) == (STBIR__ARRAY_SIZE( split_info->profile.array )-1) )?1:-1]; + typedef int testb[ (sizeof( split_info->profile.array ) == (sizeof(split_info->profile.named)) )?1:-1]; + typedef int testc[ (sizeof( info->clocks ) >= (sizeof(split_info->profile.named)) )?1:-1]; + + if ( split_start == -1 ) + { + split_start = 0; + split_count = resize->samplers->splits; + } + + if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) ) + { + info->total_clocks = 0; + info->descriptions = 0; + info->count = 0; + return; + } + + split_info = resize->samplers->split_info + split_start; + + // sum up the profile from all the splits + for( i = 0 ; i < STBIR__ARRAY_SIZE( descriptions ) ; i++ ) + { + stbir_uint64 sum = 0; + for( s = 0 ; s < split_count ; s++ ) + sum += split_info[s].profile.array[i+1]; + info->clocks[i] = sum; + } + + info->total_clocks = split_info->profile.named.total; + info->descriptions = descriptions; + info->count = STBIR__ARRAY_SIZE( descriptions ); +} + +STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize ) +{ + stbir_resize_split_profile_info( info, resize, -1, 0 ); +} + +#endif // STBIR_PROFILE + +#undef STBIR_BGR +#undef STBIR_1CHANNEL +#undef STBIR_2CHANNEL +#undef STBIR_RGB +#undef STBIR_RGBA +#undef STBIR_4CHANNEL +#undef STBIR_BGRA +#undef STBIR_ARGB +#undef STBIR_ABGR +#undef STBIR_RA +#undef STBIR_AR +#undef STBIR_RGBA_PM +#undef STBIR_BGRA_PM +#undef STBIR_ARGB_PM +#undef STBIR_ABGR_PM +#undef STBIR_RA_PM +#undef STBIR_AR_PM + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION + +#else // STB_IMAGE_RESIZE_HORIZONTALS&STB_IMAGE_RESIZE_DO_VERTICALS + +// we reinclude the header file to define all the horizontal functions +// specializing each function for the number of coeffs is 20-40% faster *OVERALL* + +// by including the header file again this way, we can still debug the functions + +#define STBIR_strs_join2( start, mid, end ) start##mid##end +#define STBIR_strs_join1( start, mid, end ) STBIR_strs_join2( start, mid, end ) + +#define STBIR_strs_join24( start, mid1, mid2, end ) start##mid1##mid2##end +#define STBIR_strs_join14( start, mid1, mid2, end ) STBIR_strs_join24( start, mid1, mid2, end ) + +#ifdef STB_IMAGE_RESIZE_DO_CODERS + +#ifdef stbir__decode_suffix +#define STBIR__CODER_NAME( name ) STBIR_strs_join1( name, _, stbir__decode_suffix ) +#else +#define STBIR__CODER_NAME( name ) name +#endif + +#ifdef stbir__decode_swizzle +#define stbir__decode_simdf8_flip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3),stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg) +#define stbir__decode_simdf4_flip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg) +#define stbir__encode_simdf8_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3),stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg) +#define stbir__encode_simdf4_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg) +#else +#define stbir__decode_order0 0 +#define stbir__decode_order1 1 +#define stbir__decode_order2 2 +#define stbir__decode_order3 3 +#define stbir__encode_order0 0 +#define stbir__encode_order1 1 +#define stbir__encode_order2 2 +#define stbir__encode_order3 3 +#define stbir__decode_simdf8_flip(reg) +#define stbir__decode_simdf4_flip(reg) +#define stbir__encode_simdf8_unflip(reg) +#define stbir__encode_simdf4_unflip(reg) +#endif + +#ifdef STBIR_SIMD8 +#define stbir__encode_simdfX_unflip stbir__encode_simdf8_unflip +#else +#define stbir__encode_simdfX_unflip stbir__encode_simdf4_unflip +#endif + +static void STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const*)inputp; + + #ifdef STBIR_SIMD + unsigned char const * end_input_m16 = input + width_times_channels - 16; + if ( width_times_channels >= 16 ) + { + decode_end -= 16; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o0,o1; + stbir__simdf8 of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u8_to_u32( o0, o1, i ); + stbir__simdi8_convert_i32_to_float( of0, o0 ); + stbir__simdi8_convert_i32_to_float( of1, o1 ); + stbir__simdf8_mult( of0, of0, STBIR_max_uint8_as_float_inverted8); + stbir__simdf8_mult( of1, of1, STBIR_max_uint8_as_float_inverted8); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode + 0, of0 ); + stbir__simdf8_store( decode + 8, of1 ); + #else + stbir__simdi i, o0, o1, o2, o3; + stbir__simdf of0, of1, of2, of3; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdi_convert_i32_to_float( of2, o2 ); + stbir__simdi_convert_i32_to_float( of3, o3 ); + stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of2, of2, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of3, of3, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + stbir__simdf_store( decode + 8, of2 ); + stbir__simdf_store( decode + 12, of3 ); + #endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted; + decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted; + decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted; + decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint8_as_float_inverted; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted; + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_linear_scaled )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp; + unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdi i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode ); + stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + #ifdef STBIR_SIMD8 + stbir__simdf8_pack_to_16bytes( i, e0, e1 ); + stbir__simdi_store( output, i ); + #else + stbir__simdf_pack_to_8bytes( i, e0, e1 ); + stbir__simdi_store2( output, i ); + #endif + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + stbir__simdi i0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); + stbir__simdf_madd( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), e0 ); + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4 + *(int*)(output-4) = stbir__simdi_to_int( i0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + stbir__simdf e0; + STBIR_NO_UNROLL(encode); + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_uint8( e0 ); + #if stbir__coder_min_num >= 2 + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_uint8( e0 ); + #endif + #if stbir__coder_min_num >= 3 + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_uint8( e0 ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f; + f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f; + f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f; + f = encode[stbir__encode_order3] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const*)inputp; + + #ifdef STBIR_SIMD + unsigned char const * end_input_m16 = input + width_times_channels - 16; + if ( width_times_channels >= 16 ) + { + decode_end -= 16; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o0,o1; + stbir__simdf8 of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u8_to_u32( o0, o1, i ); + stbir__simdi8_convert_i32_to_float( of0, o0 ); + stbir__simdi8_convert_i32_to_float( of1, o1 ); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode + 0, of0 ); + stbir__simdf8_store( decode + 8, of1 ); + #else + stbir__simdi i, o0, o1, o2, o3; + stbir__simdf of0, of1, of2, of3; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdi_convert_i32_to_float( of2, o2 ); + stbir__simdi_convert_i32_to_float( of3, o3 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + stbir__simdf_store( decode + 8, of2 ); + stbir__simdf_store( decode + 12, of3 ); +#endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])); + decode[1-4] = ((float)(input[stbir__decode_order1])); + decode[2-4] = ((float)(input[stbir__decode_order2])); + decode[3-4] = ((float)(input[stbir__decode_order3])); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])); + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp; + unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdi i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode ); + stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + #ifdef STBIR_SIMD8 + stbir__simdf8_pack_to_16bytes( i, e0, e1 ); + stbir__simdi_store( output, i ); + #else + stbir__simdf_pack_to_8bytes( i, e0, e1 ); + stbir__simdi_store2( output, i ); + #endif + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + stbir__simdi i0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); + stbir__simdf_add( e0, STBIR__CONSTF(STBIR_simd_point5), e0 ); + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4 + *(int*)(output-4) = stbir__simdi_to_int( i0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f; + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f; + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f; + f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ]; + decode[1-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ]; + decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ]; + decode[3-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order3 ] ]; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ]; + #if stbir__coder_min_num >= 2 + decode[1] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ]; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ]; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +#define stbir__min_max_shift20( i, f ) \ + stbir__simdf_max( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_zero )) ); \ + stbir__simdf_min( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_one )) ); \ + stbir__simdi_32shr( i, stbir_simdi_castf( f ), 20 ); + +#define stbir__scale_and_convert( i, f ) \ + stbir__simdf_madd( f, STBIR__CONSTF( STBIR_simd_point5 ), STBIR__CONSTF( STBIR_max_uint8_as_float ), f ); \ + stbir__simdf_max( f, f, stbir__simdf_zeroP() ); \ + stbir__simdf_min( f, f, STBIR__CONSTF( STBIR_max_uint8_as_float ) ); \ + stbir__simdf_convert_float_to_i32( i, f ); + +#define stbir__linear_to_srgb_finish( i, f ) \ +{ \ + stbir__simdi temp; \ + stbir__simdi_32shr( temp, stbir_simdi_castf( f ), 12 ) ; \ + stbir__simdi_and( temp, temp, STBIR__CONSTI(STBIR_mastissa_mask) ); \ + stbir__simdi_or( temp, temp, STBIR__CONSTI(STBIR_topscale) ); \ + stbir__simdi_16madd( i, i, temp ); \ + stbir__simdi_32shr( i, i, 16 ); \ +} + +#define stbir__simdi_table_lookup2( v0,v1, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ +} + +#define stbir__simdi_table_lookup3( v0,v1,v2, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1,temp2; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp2.m128i_i128 = v2; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ + v2 = temp2.m128i_i128; \ +} + +#define stbir__simdi_table_lookup4( v0,v1,v2,v3, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1,temp2,temp3; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp2.m128i_i128 = v2; \ + temp3.m128i_i128 = v3; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \ + temp3.m128i_u32[0] = table[temp3.m128i_i32[0]]; temp3.m128i_u32[1] = table[temp3.m128i_i32[1]]; temp3.m128i_u32[2] = table[temp3.m128i_i32[2]]; temp3.m128i_u32[3] = table[temp3.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ + v2 = temp2.m128i_i128; \ + v3 = temp3.m128i_i128; \ +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + STBIR_SIMD_NO_UNROLL(encode); + + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__min_max_shift20( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__min_max_shift20( i3, f3 ); + + stbir__simdi_table_lookup4( i0, i1, i2, i3, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i1, f1 ); + stbir__linear_to_srgb_finish( i2, f2 ); + stbir__linear_to_srgb_finish( i3, f3 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + encode += 16; + output += 16; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while ( output <= end_output ) + { + STBIR_SIMD_NO_UNROLL(encode); + + output[0-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] ); + output[1-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] ); + output[2-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] ); + output[3-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order3] ); + + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + STBIR_NO_UNROLL(encode); + output[0] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] ); + #if stbir__coder_min_num >= 2 + output[1] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] ); + #endif + #if stbir__coder_min_num >= 3 + output[2] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +#if ( stbir__coder_min_num == 4 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb4_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + do { + decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; + decode[1] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order1] ]; + decode[2] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order2] ]; + decode[3] = ( (float) input[stbir__decode_order3] ) * stbir__max_uint8_as_float_inverted; + input += 4; + decode += 4; + } while( decode < decode_end ); +} + + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb4_linearalpha )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__min_max_shift20( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__scale_and_convert( i3, f3 ); + + stbir__simdi_table_lookup3( i0, i1, i2, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i1, f1 ); + stbir__linear_to_srgb_finish( i2, f2 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + output += 16; + encode += 16; + + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + do { + float f; + STBIR_SIMD_NO_UNROLL(encode); + + output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] ); + output[stbir__decode_order1] = stbir__linear_to_srgb_uchar( encode[1] ); + output[stbir__decode_order2] = stbir__linear_to_srgb_uchar( encode[2] ); + + f = encode[3] * stbir__max_uint8_as_float + 0.5f; + STBIR_CLAMP(f, 0, 255); + output[stbir__decode_order3] = (unsigned char) f; + + output += 4; + encode += 4; + } while( output < end_output ); +} + +#endif + +#if ( stbir__coder_min_num == 2 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb2_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + decode += 4; + while( decode <= decode_end ) + { + decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; + decode[1-4] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted; + decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0+2] ]; + decode[3-4] = ( (float) input[stbir__decode_order1+2] ) * stbir__max_uint8_as_float_inverted; + input += 4; + decode += 4; + } + decode -= 4; + if( decode < decode_end ) + { + decode[0] = stbir__srgb_uchar_to_linear_float[ stbir__decode_order0 ]; + decode[1] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted; + } +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb2_linearalpha )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__scale_and_convert( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__scale_and_convert( i3, f3 ); + + stbir__simdi_table_lookup2( i0, i2, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i2, f2 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + output += 16; + encode += 16; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + do { + float f; + STBIR_SIMD_NO_UNROLL(encode); + + output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] ); + + f = encode[1] * stbir__max_uint8_as_float + 0.5f; + STBIR_CLAMP(f, 0, 255); + output[stbir__decode_order1] = (unsigned char) f; + + output += 2; + encode += 2; + } while( output < end_output ); +} + +#endif + +static void STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned short const * input = (unsigned short const *)inputp; + + #ifdef STBIR_SIMD + unsigned short const * end_input_m8 = input + width_times_channels - 8; + if ( width_times_channels >= 8 ) + { + decode_end -= 8; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o; + stbir__simdf8 of; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u16_to_u32( o, i ); + stbir__simdi8_convert_i32_to_float( of, o ); + stbir__simdf8_mult( of, of, STBIR_max_uint16_as_float_inverted8); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode + 0, of ); + #else + stbir__simdi i, o0, o1; + stbir__simdf of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u16_to_u32( o0,o1,i ); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted) ); + stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted)); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted; + decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted; + decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted; + decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint16_as_float_inverted; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted; + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + + +static void STBIR__CODER_NAME(stbir__encode_uint16_linear_scaled)( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp; + unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + { + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdiX i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode ); + stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_pack_to_words( i, e0, e1 ); + stbir__simdiX_store( output, i ); + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e; + stbir__simdi i; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e, encode ); + stbir__simdf_madd( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), e ); + stbir__encode_simdf4_unflip( e ); + stbir__simdf_pack_to_8words( i, e, e ); // only use first 4 + stbir__simdi_store2( output-4, i ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + stbir__simdf e; + STBIR_NO_UNROLL(encode); + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_short( e ); + #if stbir__coder_min_num >= 2 + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_short( e ); + #endif + #if stbir__coder_min_num >= 3 + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_short( e ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + STBIR_SIMD_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f; + f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f; + f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f; + f = encode[stbir__encode_order3] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned short const * input = (unsigned short const *)inputp; + + #ifdef STBIR_SIMD + unsigned short const * end_input_m8 = input + width_times_channels - 8; + if ( width_times_channels >= 8 ) + { + decode_end -= 8; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o; + stbir__simdf8 of; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u16_to_u32( o, i ); + stbir__simdi8_convert_i32_to_float( of, o ); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode + 0, of ); + #else + stbir__simdi i, o0, o1; + stbir__simdf of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u16_to_u32( o0, o1, i ); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])); + decode[1-4] = ((float)(input[stbir__decode_order1])); + decode[2-4] = ((float)(input[stbir__decode_order2])); + decode[3-4] = ((float)(input[stbir__decode_order3])); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])); + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__encode_uint16_linear)( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp; + unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + { + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdiX i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode ); + stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_pack_to_words( i, e0, e1 ); + stbir__simdiX_store( output, i ); + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e; + stbir__simdi i; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e, encode ); + stbir__simdf_add( e, STBIR__CONSTF(STBIR_simd_point5), e ); + stbir__encode_simdf4_unflip( e ); + stbir__simdf_pack_to_8words( i, e, e ); // only use first 4 + stbir__simdi_store2( output-4, i ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + STBIR_SIMD_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f; + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f; + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f; + f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + stbir__FP16 const * input = (stbir__FP16 const *)inputp; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 8 ) + { + stbir__FP16 const * end_input_m8 = input + width_times_channels - 8; + decode_end -= 8; + for(;;) + { + STBIR_NO_UNROLL(decode); + + stbir__half_to_float_SIMD( decode, input ); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of; + stbir__simdf8_load( of, decode ); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode, of ); + } + #else + { + stbir__simdf of0,of1; + stbir__simdf_load( of0, decode ); + stbir__simdf_load( of1, decode+4 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode, of0 ); + stbir__simdf_store( decode+4, of1 ); + } + #endif + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = stbir__half_to_float(input[stbir__decode_order0]); + decode[1-4] = stbir__half_to_float(input[stbir__decode_order1]); + decode[2-4] = stbir__half_to_float(input[stbir__decode_order2]); + decode[3-4] = stbir__half_to_float(input[stbir__decode_order3]); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = stbir__half_to_float(input[stbir__decode_order0]); + #if stbir__coder_min_num >= 2 + decode[1] = stbir__half_to_float(input[stbir__decode_order1]); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = stbir__half_to_float(input[stbir__decode_order2]); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_half_float_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + stbir__FP16 STBIR_SIMD_STREAMOUT_PTR( * ) output = (stbir__FP16*) outputp; + stbir__FP16 * end_output = ( (stbir__FP16*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 8 ) + { + float const * end_encode_m8 = encode + width_times_channels - 8; + end_output -= 8; + for(;;) + { + STBIR_SIMD_NO_UNROLL(encode); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of; + stbir__simdf8_load( of, encode ); + stbir__encode_simdf8_unflip( of ); + stbir__float_to_half_SIMD( output, (float*)&of ); + } + #else + { + stbir__simdf of[2]; + stbir__simdf_load( of[0], encode ); + stbir__simdf_load( of[1], encode+4 ); + stbir__encode_simdf4_unflip( of[0] ); + stbir__encode_simdf4_unflip( of[1] ); + stbir__float_to_half_SIMD( output, (float*)of ); + } + #endif + #else + stbir__float_to_half_SIMD( output, encode ); + #endif + encode += 8; + output += 8; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 8 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + STBIR_SIMD_NO_UNROLL(output); + output[0-4] = stbir__float_to_half(encode[stbir__encode_order0]); + output[1-4] = stbir__float_to_half(encode[stbir__encode_order1]); + output[2-4] = stbir__float_to_half(encode[stbir__encode_order2]); + output[3-4] = stbir__float_to_half(encode[stbir__encode_order3]); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + STBIR_NO_UNROLL(output); + output[0] = stbir__float_to_half(encode[stbir__encode_order0]); + #if stbir__coder_min_num >= 2 + output[1] = stbir__float_to_half(encode[stbir__encode_order1]); + #endif + #if stbir__coder_min_num >= 3 + output[2] = stbir__float_to_half(encode[stbir__encode_order2]); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + #ifdef stbir__decode_swizzle + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + float const * input = (float const *)inputp; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 16 ) + { + float const * end_input_m16 = input + width_times_channels - 16; + decode_end -= 16; + for(;;) + { + STBIR_NO_UNROLL(decode); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of0,of1; + stbir__simdf8_load( of0, input ); + stbir__simdf8_load( of1, input+8 ); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode, of0 ); + stbir__simdf8_store( decode+8, of1 ); + } + #else + { + stbir__simdf of0,of1,of2,of3; + stbir__simdf_load( of0, input ); + stbir__simdf_load( of1, input+4 ); + stbir__simdf_load( of2, input+8 ); + stbir__simdf_load( of3, input+12 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode, of0 ); + stbir__simdf_store( decode+4, of1 ); + stbir__simdf_store( decode+8, of2 ); + stbir__simdf_store( decode+12, of3 ); + } + #endif + #endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = input[stbir__decode_order0]; + decode[1-4] = input[stbir__decode_order1]; + decode[2-4] = input[stbir__decode_order2]; + decode[3-4] = input[stbir__decode_order3]; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = input[stbir__decode_order0]; + #if stbir__coder_min_num >= 2 + decode[1] = input[stbir__decode_order1]; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = input[stbir__decode_order2]; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif + + #else + + if ( (void*)decodep != inputp ) + STBIR_MEMCPY( decodep, inputp, width_times_channels * sizeof( float ) ); + + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_float_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + #if !defined( STBIR_FLOAT_HIGH_CLAMP ) && !defined(STBIR_FLOAT_LO_CLAMP) && !defined(stbir__decode_swizzle) + + if ( (void*)outputp != (void*) encode ) + STBIR_MEMCPY( outputp, encode, width_times_channels * sizeof( float ) ); + + #else + + float STBIR_SIMD_STREAMOUT_PTR( * ) output = (float*) outputp; + float * end_output = ( (float*) output ) + width_times_channels; + + #ifdef STBIR_FLOAT_HIGH_CLAMP + #define stbir_scalar_hi_clamp( v ) if ( v > STBIR_FLOAT_HIGH_CLAMP ) v = STBIR_FLOAT_HIGH_CLAMP; + #else + #define stbir_scalar_hi_clamp( v ) + #endif + #ifdef STBIR_FLOAT_LOW_CLAMP + #define stbir_scalar_lo_clamp( v ) if ( v < STBIR_FLOAT_LOW_CLAMP ) v = STBIR_FLOAT_LOW_CLAMP; + #else + #define stbir_scalar_lo_clamp( v ) + #endif + + #ifdef STBIR_SIMD + + #ifdef STBIR_FLOAT_HIGH_CLAMP + const stbir__simdfX high_clamp = stbir__simdf_frepX(STBIR_FLOAT_HIGH_CLAMP); + #endif + #ifdef STBIR_FLOAT_LOW_CLAMP + const stbir__simdfX low_clamp = stbir__simdf_frepX(STBIR_FLOAT_LOW_CLAMP); + #endif + + if ( width_times_channels >= ( stbir__simdfX_float_count * 2 ) ) + { + float const * end_encode_m8 = encode + width_times_channels - ( stbir__simdfX_float_count * 2 ); + end_output -= ( stbir__simdfX_float_count * 2 ); + for(;;) + { + stbir__simdfX e0, e1; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_load( e0, encode ); + stbir__simdfX_load( e1, encode+stbir__simdfX_float_count ); +#ifdef STBIR_FLOAT_HIGH_CLAMP + stbir__simdfX_min( e0, e0, high_clamp ); + stbir__simdfX_min( e1, e1, high_clamp ); +#endif +#ifdef STBIR_FLOAT_LOW_CLAMP + stbir__simdfX_max( e0, e0, low_clamp ); + stbir__simdfX_max( e1, e1, low_clamp ); +#endif + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_store( output, e0 ); + stbir__simdfX_store( output+stbir__simdfX_float_count, e1 ); + encode += stbir__simdfX_float_count * 2; + output += stbir__simdfX_float_count * 2; + if ( output < end_output ) + continue; + if ( output == ( end_output + ( stbir__simdfX_float_count * 2 ) ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); +#ifdef STBIR_FLOAT_HIGH_CLAMP + stbir__simdf_min( e0, e0, high_clamp ); +#endif +#ifdef STBIR_FLOAT_LOW_CLAMP + stbir__simdf_max( e0, e0, low_clamp ); +#endif + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_store( output-4, e0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float e; + STBIR_SIMD_NO_UNROLL(encode); + e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0-4] = e; + e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1-4] = e; + e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2-4] = e; + e = encode[ stbir__encode_order3 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[3-4] = e; + output += 4; + encode += 4; + } + output -= 4; + + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float e; + STBIR_NO_UNROLL(encode); + e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0] = e; + #if stbir__coder_min_num >= 2 + e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1] = e; + #endif + #if stbir__coder_min_num >= 3 + e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2] = e; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #endif +} + +#undef stbir__decode_suffix +#undef stbir__decode_simdf8_flip +#undef stbir__decode_simdf4_flip +#undef stbir__decode_order0 +#undef stbir__decode_order1 +#undef stbir__decode_order2 +#undef stbir__decode_order3 +#undef stbir__encode_order0 +#undef stbir__encode_order1 +#undef stbir__encode_order2 +#undef stbir__encode_order3 +#undef stbir__encode_simdf8_unflip +#undef stbir__encode_simdf4_unflip +#undef stbir__encode_simdfX_unflip +#undef STBIR__CODER_NAME +#undef stbir__coder_min_num +#undef stbir__decode_swizzle +#undef stbir_scalar_hi_clamp +#undef stbir_scalar_lo_clamp +#undef STB_IMAGE_RESIZE_DO_CODERS + +#elif defined( STB_IMAGE_RESIZE_DO_VERTICALS) + +#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#define STBIR_chans( start, end ) STBIR_strs_join14(start,STBIR__vertical_channels,end,_cont) +#else +#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__vertical_channels,end) +#endif + +#if STBIR__vertical_channels >= 1 +#define stbIF0( code ) code +#else +#define stbIF0( code ) +#endif +#if STBIR__vertical_channels >= 2 +#define stbIF1( code ) code +#else +#define stbIF1( code ) +#endif +#if STBIR__vertical_channels >= 3 +#define stbIF2( code ) code +#else +#define stbIF2( code ) +#endif +#if STBIR__vertical_channels >= 4 +#define stbIF3( code ) code +#else +#define stbIF3( code ) +#endif +#if STBIR__vertical_channels >= 5 +#define stbIF4( code ) code +#else +#define stbIF4( code ) +#endif +#if STBIR__vertical_channels >= 6 +#define stbIF5( code ) code +#else +#define stbIF5( code ) +#endif +#if STBIR__vertical_channels >= 7 +#define stbIF6( code ) code +#else +#define stbIF6( code ) +#endif +#if STBIR__vertical_channels >= 8 +#define stbIF7( code ) code +#else +#define stbIF7( code ) +#endif + +static void STBIR_chans( stbir__vertical_scatter_with_,_coeffs)( float ** outputs, float const * vertical_coefficients, float const * input, float const * input_end ) +{ + stbIF0( float STBIR_SIMD_STREAMOUT_PTR( * ) output0 = outputs[0]; float c0s = vertical_coefficients[0]; ) + stbIF1( float STBIR_SIMD_STREAMOUT_PTR( * ) output1 = outputs[1]; float c1s = vertical_coefficients[1]; ) + stbIF2( float STBIR_SIMD_STREAMOUT_PTR( * ) output2 = outputs[2]; float c2s = vertical_coefficients[2]; ) + stbIF3( float STBIR_SIMD_STREAMOUT_PTR( * ) output3 = outputs[3]; float c3s = vertical_coefficients[3]; ) + stbIF4( float STBIR_SIMD_STREAMOUT_PTR( * ) output4 = outputs[4]; float c4s = vertical_coefficients[4]; ) + stbIF5( float STBIR_SIMD_STREAMOUT_PTR( * ) output5 = outputs[5]; float c5s = vertical_coefficients[5]; ) + stbIF6( float STBIR_SIMD_STREAMOUT_PTR( * ) output6 = outputs[6]; float c6s = vertical_coefficients[6]; ) + stbIF7( float STBIR_SIMD_STREAMOUT_PTR( * ) output7 = outputs[7]; float c7s = vertical_coefficients[7]; ) + + #ifdef STBIR_SIMD + { + stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); ) + stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); ) + stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); ) + stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); ) + stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); ) + stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); ) + stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); ) + stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); ) + while ( ( (char*)input_end - (char*) input ) >= (16*stbir__simdfX_float_count) ) + { + stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3; + STBIR_SIMD_NO_UNROLL(output0); + + stbir__simdfX_load( r0, input ); stbir__simdfX_load( r1, input+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input+(3*stbir__simdfX_float_count) ); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdfX_load( o0, output0 ); stbir__simdfX_load( o1, output0+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 ); + stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); ) + stbIF1( stbir__simdfX_load( o0, output1 ); stbir__simdfX_load( o1, output1+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output1+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 ); + stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); ) + stbIF2( stbir__simdfX_load( o0, output2 ); stbir__simdfX_load( o1, output2+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output2+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 ); + stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); ) + stbIF3( stbir__simdfX_load( o0, output3 ); stbir__simdfX_load( o1, output3+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output3+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 ); + stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); ) + stbIF4( stbir__simdfX_load( o0, output4 ); stbir__simdfX_load( o1, output4+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output4+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 ); + stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); ) + stbIF5( stbir__simdfX_load( o0, output5 ); stbir__simdfX_load( o1, output5+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output5+(2*stbir__simdfX_float_count)); stbir__simdfX_load( o3, output5+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 ); + stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); ) + stbIF6( stbir__simdfX_load( o0, output6 ); stbir__simdfX_load( o1, output6+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output6+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 ); + stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); ) + stbIF7( stbir__simdfX_load( o0, output7 ); stbir__simdfX_load( o1, output7+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output7+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 ); + stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); ) + #else + stbIF0( stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); + stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); ) + stbIF1( stbir__simdfX_mult( o0, r0, c1 ); stbir__simdfX_mult( o1, r1, c1 ); stbir__simdfX_mult( o2, r2, c1 ); stbir__simdfX_mult( o3, r3, c1 ); + stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); ) + stbIF2( stbir__simdfX_mult( o0, r0, c2 ); stbir__simdfX_mult( o1, r1, c2 ); stbir__simdfX_mult( o2, r2, c2 ); stbir__simdfX_mult( o3, r3, c2 ); + stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); ) + stbIF3( stbir__simdfX_mult( o0, r0, c3 ); stbir__simdfX_mult( o1, r1, c3 ); stbir__simdfX_mult( o2, r2, c3 ); stbir__simdfX_mult( o3, r3, c3 ); + stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); ) + stbIF4( stbir__simdfX_mult( o0, r0, c4 ); stbir__simdfX_mult( o1, r1, c4 ); stbir__simdfX_mult( o2, r2, c4 ); stbir__simdfX_mult( o3, r3, c4 ); + stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); ) + stbIF5( stbir__simdfX_mult( o0, r0, c5 ); stbir__simdfX_mult( o1, r1, c5 ); stbir__simdfX_mult( o2, r2, c5 ); stbir__simdfX_mult( o3, r3, c5 ); + stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); ) + stbIF6( stbir__simdfX_mult( o0, r0, c6 ); stbir__simdfX_mult( o1, r1, c6 ); stbir__simdfX_mult( o2, r2, c6 ); stbir__simdfX_mult( o3, r3, c6 ); + stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); ) + stbIF7( stbir__simdfX_mult( o0, r0, c7 ); stbir__simdfX_mult( o1, r1, c7 ); stbir__simdfX_mult( o2, r2, c7 ); stbir__simdfX_mult( o3, r3, c7 ); + stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); ) + #endif + + input += (4*stbir__simdfX_float_count); + stbIF0( output0 += (4*stbir__simdfX_float_count); ) stbIF1( output1 += (4*stbir__simdfX_float_count); ) stbIF2( output2 += (4*stbir__simdfX_float_count); ) stbIF3( output3 += (4*stbir__simdfX_float_count); ) stbIF4( output4 += (4*stbir__simdfX_float_count); ) stbIF5( output5 += (4*stbir__simdfX_float_count); ) stbIF6( output6 += (4*stbir__simdfX_float_count); ) stbIF7( output7 += (4*stbir__simdfX_float_count); ) + } + while ( ( (char*)input_end - (char*) input ) >= 16 ) + { + stbir__simdf o0, r0; + STBIR_SIMD_NO_UNROLL(output0); + + stbir__simdf_load( r0, input ); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdf_load( o0, output0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); ) + stbIF1( stbir__simdf_load( o0, output1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); ) + stbIF2( stbir__simdf_load( o0, output2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); ) + stbIF3( stbir__simdf_load( o0, output3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); ) + stbIF4( stbir__simdf_load( o0, output4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); ) + stbIF5( stbir__simdf_load( o0, output5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); ) + stbIF6( stbir__simdf_load( o0, output6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); ) + stbIF7( stbir__simdf_load( o0, output7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); ) + #else + stbIF0( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); ) + stbIF1( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); ) + stbIF2( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); ) + stbIF3( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); ) + stbIF4( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); ) + stbIF5( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); ) + stbIF6( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); ) + stbIF7( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); ) + #endif + + input += 4; + stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; ) + } + } + #else + while ( ( (char*)input_end - (char*) input ) >= 16 ) + { + float r0, r1, r2, r3; + STBIR_NO_UNROLL(input); + + r0 = input[0], r1 = input[1], r2 = input[2], r3 = input[3]; + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( output0[0] += ( r0 * c0s ); output0[1] += ( r1 * c0s ); output0[2] += ( r2 * c0s ); output0[3] += ( r3 * c0s ); ) + stbIF1( output1[0] += ( r0 * c1s ); output1[1] += ( r1 * c1s ); output1[2] += ( r2 * c1s ); output1[3] += ( r3 * c1s ); ) + stbIF2( output2[0] += ( r0 * c2s ); output2[1] += ( r1 * c2s ); output2[2] += ( r2 * c2s ); output2[3] += ( r3 * c2s ); ) + stbIF3( output3[0] += ( r0 * c3s ); output3[1] += ( r1 * c3s ); output3[2] += ( r2 * c3s ); output3[3] += ( r3 * c3s ); ) + stbIF4( output4[0] += ( r0 * c4s ); output4[1] += ( r1 * c4s ); output4[2] += ( r2 * c4s ); output4[3] += ( r3 * c4s ); ) + stbIF5( output5[0] += ( r0 * c5s ); output5[1] += ( r1 * c5s ); output5[2] += ( r2 * c5s ); output5[3] += ( r3 * c5s ); ) + stbIF6( output6[0] += ( r0 * c6s ); output6[1] += ( r1 * c6s ); output6[2] += ( r2 * c6s ); output6[3] += ( r3 * c6s ); ) + stbIF7( output7[0] += ( r0 * c7s ); output7[1] += ( r1 * c7s ); output7[2] += ( r2 * c7s ); output7[3] += ( r3 * c7s ); ) + #else + stbIF0( output0[0] = ( r0 * c0s ); output0[1] = ( r1 * c0s ); output0[2] = ( r2 * c0s ); output0[3] = ( r3 * c0s ); ) + stbIF1( output1[0] = ( r0 * c1s ); output1[1] = ( r1 * c1s ); output1[2] = ( r2 * c1s ); output1[3] = ( r3 * c1s ); ) + stbIF2( output2[0] = ( r0 * c2s ); output2[1] = ( r1 * c2s ); output2[2] = ( r2 * c2s ); output2[3] = ( r3 * c2s ); ) + stbIF3( output3[0] = ( r0 * c3s ); output3[1] = ( r1 * c3s ); output3[2] = ( r2 * c3s ); output3[3] = ( r3 * c3s ); ) + stbIF4( output4[0] = ( r0 * c4s ); output4[1] = ( r1 * c4s ); output4[2] = ( r2 * c4s ); output4[3] = ( r3 * c4s ); ) + stbIF5( output5[0] = ( r0 * c5s ); output5[1] = ( r1 * c5s ); output5[2] = ( r2 * c5s ); output5[3] = ( r3 * c5s ); ) + stbIF6( output6[0] = ( r0 * c6s ); output6[1] = ( r1 * c6s ); output6[2] = ( r2 * c6s ); output6[3] = ( r3 * c6s ); ) + stbIF7( output7[0] = ( r0 * c7s ); output7[1] = ( r1 * c7s ); output7[2] = ( r2 * c7s ); output7[3] = ( r3 * c7s ); ) + #endif + + input += 4; + stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; ) + } + #endif + while ( input < input_end ) + { + float r = input[0]; + STBIR_NO_UNROLL(output0); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( output0[0] += ( r * c0s ); ) + stbIF1( output1[0] += ( r * c1s ); ) + stbIF2( output2[0] += ( r * c2s ); ) + stbIF3( output3[0] += ( r * c3s ); ) + stbIF4( output4[0] += ( r * c4s ); ) + stbIF5( output5[0] += ( r * c5s ); ) + stbIF6( output6[0] += ( r * c6s ); ) + stbIF7( output7[0] += ( r * c7s ); ) + #else + stbIF0( output0[0] = ( r * c0s ); ) + stbIF1( output1[0] = ( r * c1s ); ) + stbIF2( output2[0] = ( r * c2s ); ) + stbIF3( output3[0] = ( r * c3s ); ) + stbIF4( output4[0] = ( r * c4s ); ) + stbIF5( output5[0] = ( r * c5s ); ) + stbIF6( output6[0] = ( r * c6s ); ) + stbIF7( output7[0] = ( r * c7s ); ) + #endif + + ++input; + stbIF0( ++output0; ) stbIF1( ++output1; ) stbIF2( ++output2; ) stbIF3( ++output3; ) stbIF4( ++output4; ) stbIF5( ++output5; ) stbIF6( ++output6; ) stbIF7( ++output7; ) + } +} + +static void STBIR_chans( stbir__vertical_gather_with_,_coeffs)( float * outputp, float const * vertical_coefficients, float const ** inputs, float const * input0_end ) +{ + float STBIR_SIMD_STREAMOUT_PTR( * ) output = outputp; + + stbIF0( float const * input0 = inputs[0]; float c0s = vertical_coefficients[0]; ) + stbIF1( float const * input1 = inputs[1]; float c1s = vertical_coefficients[1]; ) + stbIF2( float const * input2 = inputs[2]; float c2s = vertical_coefficients[2]; ) + stbIF3( float const * input3 = inputs[3]; float c3s = vertical_coefficients[3]; ) + stbIF4( float const * input4 = inputs[4]; float c4s = vertical_coefficients[4]; ) + stbIF5( float const * input5 = inputs[5]; float c5s = vertical_coefficients[5]; ) + stbIF6( float const * input6 = inputs[6]; float c6s = vertical_coefficients[6]; ) + stbIF7( float const * input7 = inputs[7]; float c7s = vertical_coefficients[7]; ) + +#if ( STBIR__vertical_channels == 1 ) && !defined(STB_IMAGE_RESIZE_VERTICAL_CONTINUE) + // check single channel one weight + if ( ( c0s >= (1.0f-0.000001f) ) && ( c0s <= (1.0f+0.000001f) ) ) + { + STBIR_MEMCPY( output, input0, (char*)input0_end - (char*)input0 ); + return; + } +#endif + + #ifdef STBIR_SIMD + { + stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); ) + stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); ) + stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); ) + stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); ) + stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); ) + stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); ) + stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); ) + stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); ) + + while ( ( (char*)input0_end - (char*) input0 ) >= (16*stbir__simdfX_float_count) ) + { + stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3; + STBIR_SIMD_NO_UNROLL(output); + + // prefetch four loop iterations ahead (doesn't affect much for small resizes, but helps with big ones) + stbIF0( stbir__prefetch( input0 + (16*stbir__simdfX_float_count) ); ) + stbIF1( stbir__prefetch( input1 + (16*stbir__simdfX_float_count) ); ) + stbIF2( stbir__prefetch( input2 + (16*stbir__simdfX_float_count) ); ) + stbIF3( stbir__prefetch( input3 + (16*stbir__simdfX_float_count) ); ) + stbIF4( stbir__prefetch( input4 + (16*stbir__simdfX_float_count) ); ) + stbIF5( stbir__prefetch( input5 + (16*stbir__simdfX_float_count) ); ) + stbIF6( stbir__prefetch( input6 + (16*stbir__simdfX_float_count) ); ) + stbIF7( stbir__prefetch( input7 + (16*stbir__simdfX_float_count) ); ) + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdfX_load( o0, output ); stbir__simdfX_load( o1, output+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output+(3*stbir__simdfX_float_count) ); + stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 ); ) + #else + stbIF0( stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); ) + #endif + + stbIF1( stbir__simdfX_load( r0, input1 ); stbir__simdfX_load( r1, input1+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input1+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 ); ) + stbIF2( stbir__simdfX_load( r0, input2 ); stbir__simdfX_load( r1, input2+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input2+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 ); ) + stbIF3( stbir__simdfX_load( r0, input3 ); stbir__simdfX_load( r1, input3+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input3+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 ); ) + stbIF4( stbir__simdfX_load( r0, input4 ); stbir__simdfX_load( r1, input4+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input4+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 ); ) + stbIF5( stbir__simdfX_load( r0, input5 ); stbir__simdfX_load( r1, input5+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input5+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input5+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 ); ) + stbIF6( stbir__simdfX_load( r0, input6 ); stbir__simdfX_load( r1, input6+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input6+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 ); ) + stbIF7( stbir__simdfX_load( r0, input7 ); stbir__simdfX_load( r1, input7+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input7+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 ); ) + + stbir__simdfX_store( output, o0 ); stbir__simdfX_store( output+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output+(3*stbir__simdfX_float_count), o3 ); + output += (4*stbir__simdfX_float_count); + stbIF0( input0 += (4*stbir__simdfX_float_count); ) stbIF1( input1 += (4*stbir__simdfX_float_count); ) stbIF2( input2 += (4*stbir__simdfX_float_count); ) stbIF3( input3 += (4*stbir__simdfX_float_count); ) stbIF4( input4 += (4*stbir__simdfX_float_count); ) stbIF5( input5 += (4*stbir__simdfX_float_count); ) stbIF6( input6 += (4*stbir__simdfX_float_count); ) stbIF7( input7 += (4*stbir__simdfX_float_count); ) + } + + while ( ( (char*)input0_end - (char*) input0 ) >= 16 ) + { + stbir__simdf o0, r0; + STBIR_SIMD_NO_UNROLL(output); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdf_load( o0, output ); stbir__simdf_load( r0, input0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); ) + #else + stbIF0( stbir__simdf_load( r0, input0 ); stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); ) + #endif + stbIF1( stbir__simdf_load( r0, input1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); ) + stbIF2( stbir__simdf_load( r0, input2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); ) + stbIF3( stbir__simdf_load( r0, input3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); ) + stbIF4( stbir__simdf_load( r0, input4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); ) + stbIF5( stbir__simdf_load( r0, input5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); ) + stbIF6( stbir__simdf_load( r0, input6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); ) + stbIF7( stbir__simdf_load( r0, input7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); ) + + stbir__simdf_store( output, o0 ); + output += 4; + stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; ) + } + } + #else + while ( ( (char*)input0_end - (char*) input0 ) >= 16 ) + { + float o0, o1, o2, o3; + STBIR_NO_UNROLL(output); + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( o0 = output[0] + input0[0] * c0s; o1 = output[1] + input0[1] * c0s; o2 = output[2] + input0[2] * c0s; o3 = output[3] + input0[3] * c0s; ) + #else + stbIF0( o0 = input0[0] * c0s; o1 = input0[1] * c0s; o2 = input0[2] * c0s; o3 = input0[3] * c0s; ) + #endif + stbIF1( o0 += input1[0] * c1s; o1 += input1[1] * c1s; o2 += input1[2] * c1s; o3 += input1[3] * c1s; ) + stbIF2( o0 += input2[0] * c2s; o1 += input2[1] * c2s; o2 += input2[2] * c2s; o3 += input2[3] * c2s; ) + stbIF3( o0 += input3[0] * c3s; o1 += input3[1] * c3s; o2 += input3[2] * c3s; o3 += input3[3] * c3s; ) + stbIF4( o0 += input4[0] * c4s; o1 += input4[1] * c4s; o2 += input4[2] * c4s; o3 += input4[3] * c4s; ) + stbIF5( o0 += input5[0] * c5s; o1 += input5[1] * c5s; o2 += input5[2] * c5s; o3 += input5[3] * c5s; ) + stbIF6( o0 += input6[0] * c6s; o1 += input6[1] * c6s; o2 += input6[2] * c6s; o3 += input6[3] * c6s; ) + stbIF7( o0 += input7[0] * c7s; o1 += input7[1] * c7s; o2 += input7[2] * c7s; o3 += input7[3] * c7s; ) + output[0] = o0; output[1] = o1; output[2] = o2; output[3] = o3; + output += 4; + stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; ) + } + #endif + while ( input0 < input0_end ) + { + float o0; + STBIR_NO_UNROLL(output); + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( o0 = output[0] + input0[0] * c0s; ) + #else + stbIF0( o0 = input0[0] * c0s; ) + #endif + stbIF1( o0 += input1[0] * c1s; ) + stbIF2( o0 += input2[0] * c2s; ) + stbIF3( o0 += input3[0] * c3s; ) + stbIF4( o0 += input4[0] * c4s; ) + stbIF5( o0 += input5[0] * c5s; ) + stbIF6( o0 += input6[0] * c6s; ) + stbIF7( o0 += input7[0] * c7s; ) + output[0] = o0; + ++output; + stbIF0( ++input0; ) stbIF1( ++input1; ) stbIF2( ++input2; ) stbIF3( ++input3; ) stbIF4( ++input4; ) stbIF5( ++input5; ) stbIF6( ++input6; ) stbIF7( ++input7; ) + } +} + +#undef stbIF0 +#undef stbIF1 +#undef stbIF2 +#undef stbIF3 +#undef stbIF4 +#undef stbIF5 +#undef stbIF6 +#undef stbIF7 +#undef STB_IMAGE_RESIZE_DO_VERTICALS +#undef STBIR__vertical_channels +#undef STB_IMAGE_RESIZE_DO_HORIZONTALS +#undef STBIR_strs_join24 +#undef STBIR_strs_join14 +#undef STBIR_chans +#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#undef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#endif + +#else // !STB_IMAGE_RESIZE_DO_VERTICALS + +#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__horizontal_channels,end) + +#ifndef stbir__2_coeff_only +#define stbir__2_coeff_only() \ + stbir__1_coeff_only(); \ + stbir__1_coeff_remnant(1); +#endif + +#ifndef stbir__2_coeff_remnant +#define stbir__2_coeff_remnant( ofs ) \ + stbir__1_coeff_remnant(ofs); \ + stbir__1_coeff_remnant((ofs)+1); +#endif + +#ifndef stbir__3_coeff_only +#define stbir__3_coeff_only() \ + stbir__2_coeff_only(); \ + stbir__1_coeff_remnant(2); +#endif + +#ifndef stbir__3_coeff_remnant +#define stbir__3_coeff_remnant( ofs ) \ + stbir__2_coeff_remnant(ofs); \ + stbir__1_coeff_remnant((ofs)+2); +#endif + +#ifndef stbir__3_coeff_setup +#define stbir__3_coeff_setup() +#endif + +#ifndef stbir__4_coeff_start +#define stbir__4_coeff_start() \ + stbir__2_coeff_only(); \ + stbir__2_coeff_remnant(2); +#endif + +#ifndef stbir__4_coeff_continue_from_4 +#define stbir__4_coeff_continue_from_4( ofs ) \ + stbir__2_coeff_remnant(ofs); \ + stbir__2_coeff_remnant((ofs)+2); +#endif + +#ifndef stbir__store_output_tiny +#define stbir__store_output_tiny stbir__store_output +#endif + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_1_coeff)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__1_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_2_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__2_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_3_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__3_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_4_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_5_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__1_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_6_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__2_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_7_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + stbir__3_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_8_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_9_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__1_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_10_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__2_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_11_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__3_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_12_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__4_coeff_continue_from_4(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod0 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 4 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod1 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 5 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__1_coeff_remnant( 4 ); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod2 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 6 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__2_coeff_remnant( 4 ); + + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod3 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 7 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__3_coeff_remnant( 4 ); + + stbir__store_output(); + } while ( output < output_end ); +} + +static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_funcs)[4]= +{ + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod0), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod1), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod2), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod3), +}; + +static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_funcs)[12]= +{ + STBIR_chans(stbir__horizontal_gather_,_channels_with_1_coeff), + STBIR_chans(stbir__horizontal_gather_,_channels_with_2_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_3_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_4_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_5_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_6_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_7_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_8_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_9_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_10_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_11_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_12_coeffs), +}; + +#undef STBIR__horizontal_channels +#undef STB_IMAGE_RESIZE_DO_HORIZONTALS +#undef stbir__1_coeff_only +#undef stbir__1_coeff_remnant +#undef stbir__2_coeff_only +#undef stbir__2_coeff_remnant +#undef stbir__3_coeff_only +#undef stbir__3_coeff_remnant +#undef stbir__3_coeff_setup +#undef stbir__4_coeff_start +#undef stbir__4_coeff_continue_from_4 +#undef stbir__store_output +#undef stbir__store_output_tiny +#undef STBIR_chans + +#endif // HORIZONALS + +#undef STBIR_strs_join2 +#undef STBIR_strs_join1 + +#endif // STB_IMAGE_RESIZE_DO_HORIZONTALS/VERTICALS/CODERS + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/rtextures.c b/src/rtextures.c index 8624bbd48..bf1b35e40 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -213,7 +213,7 @@ #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size)) #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr)) #define STB_IMAGE_RESIZE_IMPLEMENTATION -#include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()] +#include "external/stb_image_resize2.h" // Required for: stbir_resize_uint8_linear() [ImageResize()] #if defined(SUPPORT_FILEFORMAT_SVG) #define NANOSVG_IMPLEMENTATION // Expands implementation @@ -1624,10 +1624,10 @@ void ImageResize(Image *image, int newWidth, int newHeight) switch (image->format) { - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 1); break; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 2); break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 3); break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 4); break; + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)1); break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)2); break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)3); break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)4); break; default: break; } @@ -1643,7 +1643,7 @@ void ImageResize(Image *image, int newWidth, int newHeight) Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); // NOTE: Color data is cast to (unsigned char *), there shouldn't been any problem... - stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4); + stbir_resize_uint8_linear((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, (stbir_pixel_layout)4); int format = image->format; From f3c27ec157f3a3e914f8872714173f87e7ea7751 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:53:31 -0300 Subject: [PATCH 0038/1037] Fix `android`, `drm` compilation issue on `InitWindow` (#3407) * Fix drm compilation issue on InitWindow * Fix android compilation issue on InitWindow --- src/rcore_android.c | 10 +++++----- src/rcore_drm.c | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index 5f6f34bab..39e4694c4 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -190,7 +190,7 @@ void InitWindow(int width, int height, const char *title) CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; - + // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -228,12 +228,12 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = platform.app->activity->internalDataPath; - + // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED); // false - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED); // true - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED); // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); diff --git a/src/rcore_drm.c b/src/rcore_drm.c index c0e88c723..2644c5e73 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -228,16 +228,16 @@ void InitWindow(int width, int height, const char *title) // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED); // false - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED); // true - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED); // false - + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false + // Initialize hi-res timer InitTimer(); - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + // Initialize raw input system InitEvdevInput(); // Evdev inputs initialization InitGamepad(); // Gamepad init From 36abc48cf8abe008f7f02516dcf1c1985517aee4 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Fri, 13 Oct 2023 19:54:00 +0200 Subject: [PATCH 0039/1037] Normalize `gestureEvent.position` coordinates (#3406) Fixed the fact that coordinates were not normalized on Android, preventing detection of `GESTURE_DOUBLE_TAP` --- src/rcore_android.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rcore_android.c b/src/rcore_android.c index 39e4694c4..3d9fab06a 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -1152,6 +1152,8 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) { gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; gestureEvent.position[i] = CORE.Input.Touch.position[i]; + gestureEvent.position[i].x /= (float)GetScreenWidth(); + gestureEvent.position[i].y /= (float)GetScreenHeight(); } // Gesture data is sent to gestures system for processing From 5a0d9c8d43d212892e421e1cbb532fea508d8692 Mon Sep 17 00:00:00 2001 From: Daniil Kisel <56605335+KislyjKisel@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:54:43 +0300 Subject: [PATCH 0040/1037] Fix `UpdateSound` parameter name (#3405) --- src/raudio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index c2590e302..a8d1b40e2 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -981,14 +981,14 @@ void UnloadSoundAlias(Sound alias) } // Update sound buffer with new data -void UpdateSound(Sound sound, const void *data, int sampleCount) +void UpdateSound(Sound sound, const void *data, int frameCount) { if (sound.stream.buffer != NULL) { StopAudioBuffer(sound.stream.buffer); // TODO: May want to lock/unlock this since this data buffer is read at mixing time - memcpy(sound.stream.buffer->data, data, sampleCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn)); + memcpy(sound.stream.buffer->data, data, frameCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn)); } } From 4981acb241d18afc4d0db3497ecbe37d1f31808e Mon Sep 17 00:00:00 2001 From: Purple4pur <49893724+purple4pur@users.noreply.github.com> Date: Sat, 14 Oct 2023 01:55:52 +0800 Subject: [PATCH 0041/1037] fix zig syntax errors in examples, and make it install executables correctly (#3395) --- build.zig | 2 +- examples/build.zig | 36 ++++++++++++++++++++---------------- src/build.zig | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/build.zig b/build.zig index 12c0513f6..21638601d 100644 --- a/build.zig +++ b/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) pub fn build(b: *std.Build) void { raylib.build(b); } diff --git a/examples/build.zig b/examples/build.zig index 6e13ab3da..94ecc6783 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); @@ -11,7 +11,7 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); while (try iter.next()) |entry| { - if (entry.kind != .File) continue; + if (entry.kind != .file) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; const path = try std.fs.path.join(b.allocator, &.{ module, entry.name }); @@ -24,26 +24,26 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT .target = target, .optimize = optimize, }); - exe.addCSourceFile(path, &[_][]const u8{}); + exe.addCSourceFile(.{ .file = .{ .path = path }, .flags = &.{} }); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => "../src/zig-out/lib/raylib.lib", - .linux => "../src/zig-out/lib/libraylib.a", - .macos => "../src/zig-out/lib/libraylib.a", - .emscripten => "../src/zig-out/lib/libraylib.a", + .windows => .{ .path = "../zig-out/lib/raylib.lib" }, + .linux => .{ .path = "../zig-out/lib/libraylib.a" }, + .macos => .{ .path = "../zig-out/lib/libraylib.a" }, + .emscripten => .{ .path = "../zig-out/lib/libraylib.a" }, else => @panic("Unsupported OS"), }); - exe.addIncludePath("../src"); - exe.addIncludePath("../src/external"); - exe.addIncludePath("../src/external/glfw/include"); + exe.addIncludePath(.{ .path = "../src" }); + exe.addIncludePath(.{ .path = "../src/external" }); + exe.addIncludePath(.{ .path = "../src/external/glfw/include" }); switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludePath("external/glfw/deps/mingw"); + exe.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); exe.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -71,11 +71,15 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - b.installArtifact(exe); - var run = b.addRunArtifact(exe); - run.cwd = module; - b.step(name, name).dependOn(&run.step); - all.dependOn(&exe.step); + const install_cmd = b.addInstallArtifact(exe, .{}); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(&install_cmd.step); + + const run_step = b.step(name, name); + run_step.dependOn(&run_cmd.step); + + all.dependOn(&install_cmd.step); } return all; } diff --git a/src/build.zig b/src/build.zig index 27250f5ff..53e074245 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", From 2f08f435b91a08c57bf7e381a46381770a00791e Mon Sep 17 00:00:00 2001 From: Daniil Kisel <56605335+KislyjKisel@users.noreply.github.com> Date: Fri, 13 Oct 2023 21:54:15 +0300 Subject: [PATCH 0042/1037] Add Raylib.lean to BINDINGS.md (#3409) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 47a1c0a27..e08f8f79e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -81,6 +81,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayed-bqn | **auto** | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | | rayjs | 4.6-dev | [QuickJS](https://bellard.org/quickjs/) | MIT | https://github.com/mode777/rayjs | | raylib-raku | **auto** | [Raku](https://www.raku.org/) | Artistic License 2.0 | https://github.com/vushu/raylib-raku | +| Raylib.lean | 4.5 | [Lean4](https://lean-lang.org/) | BSD-3-Clause | https://github.com/KislyjKisel/Raylib.lean | ### Utility Wrapers These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. From 005ba155c0b4d8e065e6e3ffe2cdd82bb41bf200 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 10:56:09 +0200 Subject: [PATCH 0043/1037] Minor tweaks --- src/rcore.c | 4 ++-- src/rcore_desktop.c | 2 +- src/rcore_web.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 3efa67b2c..b3e07f95a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2812,7 +2812,7 @@ static void RecordAutomationEvent(unsigned int frame) // INPUT_GAMEPAD_CONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && - (CORE.Input.Gamepad.currentState[gamepad] == true)) // Check if changed to ready + (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready { // TODO: Save gamepad connect event } @@ -2821,7 +2821,7 @@ static void RecordAutomationEvent(unsigned int frame) // INPUT_GAMEPAD_DISCONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && - (CORE.Input.Gamepad.currentState[gamepad] == false)) // Check if changed to not-ready + (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready { // TODO: Save gamepad disconnect event } diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index c2e5b23f1..8fa1edd78 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1966,7 +1966,7 @@ static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffs // GLFW3 CursorEnter Callback, when cursor enters the window static void CursorEnterCallback(GLFWwindow *window, int enter) { - if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + if (enter) CORE.Input.Mouse.cursorOnScreen = true; else CORE.Input.Mouse.cursorOnScreen = false; } diff --git a/src/rcore_web.c b/src/rcore_web.c index 261498a5b..c0b7079b0 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1336,7 +1336,7 @@ static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffs // GLFW3 CursorEnter Callback, when cursor enters the window static void CursorEnterCallback(GLFWwindow *window, int enter) { - if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + if (enter) CORE.Input.Mouse.cursorOnScreen = true; else CORE.Input.Mouse.cursorOnScreen = false; } From 4521a142c35fe9fe1386d79ae783363c9c164827 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 11:48:20 +0200 Subject: [PATCH 0044/1037] tweaks --- src/rcore.c | 2 +- src/rcore_web.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b3e07f95a..d7aaa30b3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2360,7 +2360,7 @@ int GetTouchPointCount(void) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//static bool InitGraphicsDevice(int width, int height) +//static bool InitPlatform(void) // Initialize hi-resolution timer void InitTimer(void) diff --git a/src/rcore_web.c b/src/rcore_web.c index c0b7079b0..277d1378f 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1119,7 +1119,7 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified) // GLFW3 Window Maximize Callback, runs when window is maximized static void WindowMaximizeCallback(GLFWwindow *window, int maximized) { - + // TODO. } // GLFW3 WindowFocus Callback, runs when window get/lose focus @@ -1157,7 +1157,6 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths } } - // GLFW3 Keyboard Callback, runs on key pressed static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { @@ -1340,7 +1339,6 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) else CORE.Input.Mouse.cursorOnScreen = false; } - // Register fullscreen change events static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) { From b34c2ecbcb9da1d438b70acf8125ef5424744d11 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 12:49:54 +0200 Subject: [PATCH 0045/1037] WARNING: REDESIGN: `InitPlatform()` to initialize all platform data #3313 `InitGraphicsDevice()` could be confusing because the function actually initialized many things: window, graphics, inputs, callbacks, timming, storage... restructured it. --- src/rcore.c | 8 +- src/rcore_android.c | 281 ++++++++++++++++++---------------- src/rcore_desktop.c | 146 ++++++++---------- src/rcore_drm.c | 354 +++++++++++++++++++++---------------------- src/rcore_template.c | 71 ++++----- src/rcore_web.c | 198 +++++++++++------------- 6 files changed, 508 insertions(+), 550 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index d7aaa30b3..e6015b33f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2365,10 +2365,10 @@ int GetTouchPointCount(void) // Initialize hi-resolution timer void InitTimer(void) { -// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. -// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. -// High resolutions can also prevent the CPU power management system from entering power-saving modes. -// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. + // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. + // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. + // High resolutions can also prevent the CPU power management system from entering power-saving modes. + // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif diff --git a/src/rcore_android.c b/src/rcore_android.c index 3d9fab06a..3e3b8cd12 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -82,7 +82,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs @@ -172,88 +173,23 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 CORE.Input.Keyboard.exitKey = KEY_ESCAPE; CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Platform specific init window - //-------------------------------------------------------------- - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - - // Set desired windows flags before initializing anything - ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER - - int orientation = AConfiguration_getOrientation(platform.app->config); - - if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); - else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); - - // TODO: Automatic orientation doesn't seem to work - if (width <= height) - { - AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); - } - else - { - AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); - } - - //AConfiguration_getDensity(platform.app->config); - //AConfiguration_getKeyboard(platform.app->config); - //AConfiguration_getScreenSize(platform.app->config); - //AConfiguration_getScreenLong(platform.app->config); - - // Initialize App command system - // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - platform.app->onAppCmd = AndroidCommandCallback; - - // Initialize input events system - platform.app->onInputEvent = AndroidInputCallback; - - // Initialize assets manager - InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); - - // Initialize base path for storage - CORE.Storage.basePath = platform.app->activity->internalDataPath; - - // Set some default window flags - CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false - - TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Wait for window to be initialized (display and context) - while (!CORE.Window.ready) - { - // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) - { - // Process this event - if (platform.source != NULL) platform.source->process(platform.app, platform.source); - - // NOTE: Never close window, native activity is controlled by the system! - //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; - } - } + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- } @@ -279,28 +215,9 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif - // Platform specific close window - //-------------------------------------------------------------- - // Close surface, context and display - if (platform.device != EGL_NO_DISPLAY) - { - eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (platform.surface != EGL_NO_SURFACE) - { - eglDestroySurface(platform.device, platform.surface); - platform.surface = EGL_NO_SURFACE; - } - - if (platform.context != EGL_NO_CONTEXT) - { - eglDestroyContext(platform.device, platform.context); - platform.context = EGL_NO_CONTEXT; - } - - eglTerminate(platform.device); - platform.device = EGL_NO_DISPLAY; - } + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -690,25 +607,108 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) +{ + CORE.Window.currentFbo.width = CORE.Window.screen.width; + CORE.Window.currentFbo.height = CORE.Window.screen.width; + + // Set desired windows flags before initializing anything + ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + + int orientation = AConfiguration_getOrientation(platform.app->config); + + if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); + else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); + + // TODO: Automatic orientation doesn't seem to work + if (width <= height) + { + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); + } + else + { + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); + } + + //AConfiguration_getDensity(platform.app->config); + //AConfiguration_getKeyboard(platform.app->config); + //AConfiguration_getScreenSize(platform.app->config); + //AConfiguration_getScreenLong(platform.app->config); + + // Initialize App command system + // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... + platform.app->onAppCmd = AndroidCommandCallback; + + // Initialize input events system + platform.app->onInputEvent = AndroidInputCallback; + + // Initialize assets manager + InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); + + // Initialize base path for storage + CORE.Storage.basePath = platform.app->activity->internalDataPath; + + // Set some default window flags + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false + + TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Wait for window to be initialized (display and context) + while (!CORE.Window.ready) + { + // Process events loop + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) + { + // Process this event + if (platform.source != NULL) platform.source->process(platform.app, platform.source); + + // NOTE: Never close window, native activity is controlled by the system! + //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; + } + } +} + +// Close platform +static void ClosePlatform(void) +{ + // Close surface, context and display + if (platform.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (platform.surface != EGL_NO_SURFACE) + { + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; + } + + if (platform.context != EGL_NO_CONTEXT) + { + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; + } + + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; + } +} + // Initialize display device and framebuffer // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size // NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +static bool InitGraphicsDevice(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -748,7 +748,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } // Initialize the EGL device connection @@ -756,7 +756,7 @@ static bool InitGraphicsDevice(int width, int height) { // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } // Get an appropriate EGL framebuffer configuration @@ -770,7 +770,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; + return -1; } // Create an EGL window surface @@ -799,7 +799,7 @@ static bool InitGraphicsDevice(int width, int height) if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; + return -1; } else { @@ -819,19 +819,11 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - CORE.Window.ready = true; if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - return true; + return 0; } // ANDROID: Process activity lifecycle commands @@ -874,24 +866,49 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window); // Initialize graphics device (display device and OpenGL context) - InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); + InitGraphicsDevice(); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); // Initialize hi-res timer InitTimer(); - // Initialize random seed - srand((unsigned int)time(NULL)); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext LoadFontDefault(); - Rectangle rec = GetFontDefault().recs[95]; - // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering #if defined(SUPPORT_MODULE_RSHAPES) - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif + #else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes #endif #endif + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); // TODO: GPU assets reload in case of lost focus (lost context) // NOTE: This problem has been solved just unbinding and rebinding context from display diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 8fa1edd78..81da488e3 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -111,7 +111,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform // Error callback event static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error @@ -176,53 +177,31 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE structure to 0 + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 CORE.Input.Keyboard.exitKey = KEY_ESCAPE; CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; - - - // Platform specific init window - //-------------------------------------------------------------- - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - - glfwInitAllocator(&allocator); -*/ - - // Initialize graphics device - // NOTE: returns true if window and graphic device has been initialized successfully - // WARNING: Actually, all window initialization and input callbacks initialization is - // done inside InitGraphicsDevice(), this functionality should be changed! - CORE.Window.ready = InitGraphicsDevice(width, height); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- + + // Initialize rlgl default data (buffers and shaders) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -266,6 +245,9 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); } @@ -287,14 +269,9 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - // Platform specific close window - //-------------------------------------------------------------- - glfwDestroyWindow(platform.handle); - glfwTerminate(); - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -1379,34 +1356,28 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) + glfwInitAllocator(&allocator); +*/ #if defined(__APPLE__) glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); #endif - - if (!glfwInit()) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); - return false; - } + // Initialize GLFW internal global state + int result = glfwInit(); + if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } glfwDefaultWindowHints(); // Set default windows hints //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits @@ -1528,7 +1499,7 @@ static bool InitGraphicsDevice(int width, int height) if (!monitor) { TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); - return false; + return -1; } const GLFWvidmode *mode = glfwGetVideoMode(monitor); @@ -1617,7 +1588,7 @@ static bool InitGraphicsDevice(int width, int height) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return false; + return -1; } // Set window callback events @@ -1683,22 +1654,35 @@ static bool InitGraphicsDevice(int width, int height) // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - return true; + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + return 0; } +// Close platform +static void ClosePlatform(void) +{ + glfwDestroyWindow(platform.handle); + glfwTerminate(); + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif +} + + // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 2644c5e73..213f2c546 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -140,21 +140,22 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform -static void InitKeyboard(void); // Initialize raw keyboard system -static void RestoreKeyboard(void); // Restore keyboard system +static void InitKeyboard(void); // Initialize raw keyboard system +static void RestoreKeyboard(void); // Restore keyboard system #if defined(SUPPORT_SSH_KEYBOARD_RPI) -static void ProcessKeyboard(void); // Process keyboard events +static void ProcessKeyboard(void); // Process keyboard events #endif -static void InitEvdevInput(void); // Initialize evdev inputs -static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate -static void PollKeyboardEvents(void); // Process evdev keyboard events -static void *EventThread(void *arg); // Input device events reading thread +static void InitEvdevInput(void); // Initialize evdev inputs +static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate +static void PollKeyboardEvents(void); // Process evdev keyboard events +static void *EventThread(void *arg); // Input device events reading thread -static void InitGamepad(void); // Initialize raw gamepad input -static void *GamepadThread(void *arg); // Mouse reading thread +static void InitGamepad(void); // Initialize raw gamepad input +static void *GamepadThread(void *arg); // Mouse reading thread 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 @@ -204,49 +205,31 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f}; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; - - - // Platform specific init window + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; + + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- - // Initialize graphics device (display device and OpenGL context) - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); + + // Initialize rlgl default data (buffers and shaders) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); - - // Set some default window flags - CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false - - // Initialize hi-res timer - InitTimer(); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) - //-------------------------------------------------------------- - - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -289,7 +272,10 @@ void InitWindow(int width, int height, const char *title) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif - + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -315,93 +301,9 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif - // Platform specific close window - //-------------------------------------------------------------- - if (platform.prevFB) - { - drmModeRmFB(platform.fd, platform.prevFB); - platform.prevFB = 0; - } - - if (platform.prevBO) - { - gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); - platform.prevBO = NULL; - } - - if (platform.gbmSurface) - { - gbm_surface_destroy(platform.gbmSurface); - platform.gbmSurface = NULL; - } - - if (platform.gbmDevice) - { - gbm_device_destroy(platform.gbmDevice); - platform.gbmDevice = NULL; - } - - if (platform.crtc) - { - if (platform.connector) - { - drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id, - platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode); - drmModeFreeConnector(platform.connector); - platform.connector = NULL; - } - - drmModeFreeCrtc(platform.crtc); - platform.crtc = NULL; - } - - if (platform.fd != -1) - { - close(platform.fd); - platform.fd = -1; - } - - // Close surface, context and display - if (platform.device != EGL_NO_DISPLAY) - { - if (platform.surface != EGL_NO_SURFACE) - { - eglDestroySurface(platform.device, platform.surface); - platform.surface = EGL_NO_SURFACE; - } - - if (platform.context != EGL_NO_CONTEXT) - { - eglDestroyContext(platform.device, platform.context); - platform.context = EGL_NO_CONTEXT; - } - - eglTerminate(platform.device); - platform.device = EGL_NO_DISPLAY; - } - - // 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 - - CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called - - // Close the evdev keyboard - if (platform.keyboardFd != -1) - { - close(platform.keyboardFd); - platform.keyboardFd = -1; - } - - for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (platform.eventWorker[i].threadId) - { - pthread_join(platform.eventWorker[i].threadId, NULL); - } - } - - if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -808,28 +710,9 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the window minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - - CORE.Window.fullscreen = true; - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - platform.fd = -1; platform.connector = NULL; platform.modeIndex = -1; @@ -838,6 +721,9 @@ static bool InitGraphicsDevice(int width, int height) platform.gbmSurface = NULL; platform.prevBO = NULL; platform.prevFB = 0; + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; #if defined(DEFAULT_GRAPHIC_DEVICE_DRM) platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); @@ -861,14 +747,14 @@ static bool InitGraphicsDevice(int width, int height) if (platform.fd == -1) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); - return false; + return -1; } drmModeRes *res = drmModeGetResources(platform.fd); if (!res) { TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); - return false; + return -1; } TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors); @@ -897,7 +783,7 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); drmModeFreeResources(res); - return false; + return -1; } drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); @@ -905,7 +791,7 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); drmModeFreeResources(res); - return false; + return -1; } platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id); @@ -914,7 +800,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); drmModeFreeEncoder(enc); drmModeFreeResources(res); - return false; + return -1; } // If InitWindow should use the current mode find it in the connector's mode list @@ -929,7 +815,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); drmModeFreeEncoder(enc); drmModeFreeResources(res); - return false; + return -1; } CORE.Window.screen.width = CORE.Window.display.width; @@ -957,7 +843,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); drmModeFreeEncoder(enc); drmModeFreeResources(res); - return false; + return -1; } CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay; @@ -982,7 +868,7 @@ static bool InitGraphicsDevice(int width, int height) if (!platform.gbmDevice) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device"); - return false; + return -1; } platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay, @@ -990,7 +876,7 @@ static bool InitGraphicsDevice(int width, int height) if (!platform.gbmSurface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface"); - return false; + return -1; } EGLint samples = 0; @@ -1030,7 +916,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } // Initialize the EGL device connection @@ -1038,13 +924,13 @@ static bool InitGraphicsDevice(int width, int height) { // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); - return false; + return -1; } TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs); @@ -1053,7 +939,7 @@ static bool InitGraphicsDevice(int width, int height) if (!configs) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs"); - return false; + return -1; } EGLint matchingNumConfigs = 0; @@ -1061,7 +947,7 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); free(configs); - return false; + return -1; } TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs); @@ -1091,7 +977,7 @@ static bool InitGraphicsDevice(int width, int height) if (!found) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config"); - return false; + return -1; } // Set rendering API @@ -1102,7 +988,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; + return -1; } // Create an EGL window surface @@ -1111,7 +997,7 @@ static bool InitGraphicsDevice(int width, int height) if (EGL_NO_SURFACE == platform.surface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); - return false; + return -1; } // At this point we need to manage render size vs screen size @@ -1127,7 +1013,7 @@ static bool InitGraphicsDevice(int width, int height) if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; + return -1; } else { @@ -1147,19 +1033,123 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); - return true; + // Set some default window flags + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false + + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + + return 0; } +// Close platform +static void ClosePlatform(void) +{ + if (platform.prevFB) + { + drmModeRmFB(platform.fd, platform.prevFB); + platform.prevFB = 0; + } + + if (platform.prevBO) + { + gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); + platform.prevBO = NULL; + } + + if (platform.gbmSurface) + { + gbm_surface_destroy(platform.gbmSurface); + platform.gbmSurface = NULL; + } + + if (platform.gbmDevice) + { + gbm_device_destroy(platform.gbmDevice); + platform.gbmDevice = NULL; + } + + if (platform.crtc) + { + if (platform.connector) + { + drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id, + platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode); + drmModeFreeConnector(platform.connector); + platform.connector = NULL; + } + + drmModeFreeCrtc(platform.crtc); + platform.crtc = NULL; + } + + if (platform.fd != -1) + { + close(platform.fd); + platform.fd = -1; + } + + // Close surface, context and display + if (platform.device != EGL_NO_DISPLAY) + { + if (platform.surface != EGL_NO_SURFACE) + { + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; + } + + if (platform.context != EGL_NO_CONTEXT) + { + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; + } + + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; + } + + // 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 + + CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called + + // Close the evdev keyboard + if (platform.keyboardFd != -1) + { + close(platform.keyboardFd); + platform.keyboardFd = -1; + } + + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (platform.eventWorker[i].threadId) + { + pthread_join(platform.eventWorker[i].threadId, NULL); + } + } + + if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); +} + + // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { diff --git a/src/rcore_template.c b/src/rcore_template.c index 88b3c4a7e..3929de4b3 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -73,7 +73,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static bool InitGraphicsDevice(void); // Initialize graphics device //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -142,19 +143,15 @@ void InitWindow(int width, int height, const char *title) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - // Initialize hi-res timer - InitTimer(); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - //-------------------------------------------------------------- + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -198,6 +195,9 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); } @@ -597,25 +597,9 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -677,7 +661,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; + return -1; } // Create an EGL window surface @@ -706,7 +690,7 @@ static bool InitGraphicsDevice(int width, int height) if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; + return -1; } else { @@ -726,19 +710,24 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - CORE.Window.ready = true; + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); - return true; + return 0; +} + +// Close platform +static void ClosePlatform(void) +{ + // TODO: De-initialize graphics, inputs and more } // EOF diff --git a/src/rcore_web.c b/src/rcore_web.c index 277d1378f..3b9373bbf 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -87,17 +87,18 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform // Error callback event -static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error +static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error // Window callbacks events -static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized -static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored -static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized -static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus -static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window +static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized +static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window // Input callbacks events static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed @@ -107,11 +108,12 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area -// Emscripten callback events +// Emscripten window callback events static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData); static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData); static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); +// Emscripten input callback events static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); @@ -160,67 +162,32 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f}; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Platform specific init window - //-------------------------------------------------------------- - // Initialize graphics device (display device and OpenGL context) - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - - // Setup callback functions for the DOM events - emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - - // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review - // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) - // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - - // Trigger this once to get initial window sizing - EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - - // Support keyboard events -> Not used, GLFW.JS takes care of that - // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - - // Support mouse events - emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); - - // Support touch events - emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - - // Support gamepad events (not provided by GLFW3 on emscripten) - emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); - emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -264,6 +231,9 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); } @@ -285,10 +255,9 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - // Platform specific close window - //-------------------------------------------------------------- - glfwDestroyWindow(platform.handle); - glfwTerminate(); + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -814,43 +783,14 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - glfwInitAllocator(&allocator); -*/ - - if (!glfwInit()) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); - return false; - } + // Initialize GLFW internal global state + int result = glfwInit(); + if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } glfwDefaultWindowHints(); // Set default windows hints // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits @@ -1016,7 +956,7 @@ static bool InitGraphicsDevice(int width, int height) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return false; + return -1; } // WARNING: glfwCreateWindow() title doesn't work with emscripten @@ -1037,6 +977,12 @@ static bool InitGraphicsDevice(int width, int height) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need @@ -1055,22 +1001,54 @@ 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, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Setup callback functions for the DOM events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review + // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) + // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + // Trigger this once to get initial window sizing + EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + // Support keyboard events -> Not used, GLFW.JS takes care of that + // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - return true; + // Support mouse events + emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); + + // Support touch events + emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + + // Support gamepad events (not provided by GLFW3 on emscripten) + emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + + return 0; +} + +// Close platform +static void ClosePlatform(void) +{ + glfwDestroyWindow(platform.handle); + glfwTerminate(); } // GLFW3 Error Callback, runs on GLFW3 error From 54950f9a3d2df2e0f9907715d6c355133d011456 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 12:55:31 +0200 Subject: [PATCH 0046/1037] Make sure CORE.Window.ready is set --- src/rcore_desktop.c | 2 ++ src/rcore_drm.c | 2 ++ src/rcore_web.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 81da488e3..fe5a02aac 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1657,6 +1657,8 @@ static int InitPlatform(void) rlLoadExtensions(glfwGetProcAddress); if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 213f2c546..8f1bfa429 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -1035,6 +1035,8 @@ static int InitPlatform(void) if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); diff --git a/src/rcore_web.c b/src/rcore_web.c index 3b9373bbf..15c7626dd 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1002,6 +1002,8 @@ static int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); From d31b439e04d311ea8068a1d20d573b73c812c645 Mon Sep 17 00:00:00 2001 From: BeardedBread Date: Sat, 14 Oct 2023 21:10:33 +0800 Subject: [PATCH 0047/1037] Implement SetMouseCursor for PLATFORM_WEB (#3414) --- src/rcore_web.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/rcore_web.c b/src/rcore_web.c index 15c7626dd..71e818ea8 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -659,7 +659,34 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_INFO, "SetMouseCursor not implemented in rcore_web.c"); + const char *cursorName; + switch (cursor) + { + case MOUSE_CURSOR_IBEAM: cursorName = "text"; break; + case MOUSE_CURSOR_CROSSHAIR: cursorName = "crosshair"; break; + case MOUSE_CURSOR_POINTING_HAND: cursorName = "pointer"; break; + case MOUSE_CURSOR_RESIZE_EW: cursorName = "ew-resize"; break; + case MOUSE_CURSOR_RESIZE_NS: cursorName = "ns-resize"; break; + case MOUSE_CURSOR_RESIZE_NWSE: cursorName = "nwse-resize"; break; + case MOUSE_CURSOR_RESIZE_NESW: cursorName = "nesw-resize"; break; + case MOUSE_CURSOR_RESIZE_ALL: cursorName = "move"; break; + case MOUSE_CURSOR_NOT_ALLOWED: cursorName = "not-allowed"; break; + + case MOUSE_CURSOR_ARROW: // can't find a name specifically for arrow cursor + case MOUSE_CURSOR_DEFAULT: + { + cursorName = "default"; + } break; + + default: + { + TRACELOG(LOG_WARNING, "Cursor value out of bound (%d). Setting to default", cursor); + cursorName = "default"; + } break; + } + + // Set the cursor element on the CSS + EM_ASM({document.body.style.cursor = UTF8ToString($0);}, cursorName); } // Register all input events From 2498170b9525df447f63da4056e5c8c0e6d1d8fc Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:11:56 +0200 Subject: [PATCH 0048/1037] Fix screen size check in `InitPlatform()` (#3415) --- src/rcore_android.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index 3e3b8cd12..b7680adbc 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -611,7 +611,7 @@ void PollInputEvents(void) static int InitPlatform(void) { CORE.Window.currentFbo.width = CORE.Window.screen.width; - CORE.Window.currentFbo.height = CORE.Window.screen.width; + CORE.Window.currentFbo.height = CORE.Window.screen.height; // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -622,7 +622,7 @@ static int InitPlatform(void) else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); // TODO: Automatic orientation doesn't seem to work - if (width <= height) + if (CORE.Window.screen.width <= CORE.Window.screen.height) { AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); From bf639f02a8a05f678c6d2459871a9e466be34b97 Mon Sep 17 00:00:00 2001 From: Blue <69832658+bluesillybeard@users.noreply.github.com> Date: Sat, 14 Oct 2023 14:38:36 -0600 Subject: [PATCH 0049/1037] Fix raygui.c leftover from zig build (#3417) --- src/build.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/build.zig b/src/build.zig index 53e074245..5d7ddbf3f 100644 --- a/src/build.zig +++ b/src/build.zig @@ -53,12 +53,12 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, raylib_flags); } - var gen_step = std.build.Step.WriteFile.create(b); + var gen_step = b.addWriteFiles(); raylib.step.dependOn(&gen_step.step); if (options.raygui) { - _ = gen_step.add(srcdir ++ "/raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); - raylib.addCSourceFile(.{ .file = .{ .path = srcdir ++ "/raygui.c" }, .flags = raylib_flags }); + const raygui_c_path = gen_step.add("raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); + raylib.addCSourceFile(.{ .file = raygui_c_path, .flags = raylib_flags }); raylib.addIncludePath(.{ .path = srcdir }); raylib.addIncludePath(.{ .path = srcdir ++ "/../../raygui/src" }); } From b79e38109268bc23eeceb05212017d3ed1359170 Mon Sep 17 00:00:00 2001 From: BeardedBread Date: Sun, 15 Oct 2023 04:42:03 +0800 Subject: [PATCH 0050/1037] Fix SetMouseCursor implementation for PLATFORM_WEB (#3416) * Fix SetMouseCursor implementation for PLATFORM_WEB - Restrict function to only set the cursor inside the canvas * Set the CORE input mouse --- src/rcore_web.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rcore_web.c b/src/rcore_web.c index 71e818ea8..34836b7f0 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -659,6 +659,7 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { + CORE.Input.Mouse.cursor = cursor; const char *cursorName; switch (cursor) { @@ -682,11 +683,13 @@ void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "Cursor value out of bound (%d). Setting to default", cursor); cursorName = "default"; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_DEFAULT; } break; } - // Set the cursor element on the CSS - EM_ASM({document.body.style.cursor = UTF8ToString($0);}, cursorName); + // Set the cursor element on the canvas CSS + // The canvas is coded to the Id "canvas" on init + EM_ASM({document.getElementById("canvas").style.cursor = UTF8ToString($0);}, cursorName); } // Register all input events From 37e3ffcaac9be9c07a9724b334d9bca3ec8b7052 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 22:45:56 +0200 Subject: [PATCH 0051/1037] REVIEWED: `SetMouseCursor()` #3416 --- src/rcore_web.c | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/rcore_web.c b/src/rcore_web.c index 34836b7f0..af2b6e36f 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -659,37 +659,35 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { - CORE.Input.Mouse.cursor = cursor; - const char *cursorName; - switch (cursor) + if (CORE.Input.Mouse.cursor != cursor) { - case MOUSE_CURSOR_IBEAM: cursorName = "text"; break; - case MOUSE_CURSOR_CROSSHAIR: cursorName = "crosshair"; break; - case MOUSE_CURSOR_POINTING_HAND: cursorName = "pointer"; break; - case MOUSE_CURSOR_RESIZE_EW: cursorName = "ew-resize"; break; - case MOUSE_CURSOR_RESIZE_NS: cursorName = "ns-resize"; break; - case MOUSE_CURSOR_RESIZE_NWSE: cursorName = "nwse-resize"; break; - case MOUSE_CURSOR_RESIZE_NESW: cursorName = "nesw-resize"; break; - case MOUSE_CURSOR_RESIZE_ALL: cursorName = "move"; break; - case MOUSE_CURSOR_NOT_ALLOWED: cursorName = "not-allowed"; break; + const char *cursorName = NULL; + CORE.Input.Mouse.cursor = cursor; - case MOUSE_CURSOR_ARROW: // can't find a name specifically for arrow cursor - case MOUSE_CURSOR_DEFAULT: + switch (cursor) { - cursorName = "default"; - } break; + case MOUSE_CURSOR_IBEAM: cursorName = "text"; break; + case MOUSE_CURSOR_CROSSHAIR: cursorName = "crosshair"; break; + case MOUSE_CURSOR_POINTING_HAND: cursorName = "pointer"; break; + case MOUSE_CURSOR_RESIZE_EW: cursorName = "ew-resize"; break; + case MOUSE_CURSOR_RESIZE_NS: cursorName = "ns-resize"; break; + case MOUSE_CURSOR_RESIZE_NWSE: cursorName = "nwse-resize"; break; + case MOUSE_CURSOR_RESIZE_NESW: cursorName = "nesw-resize"; break; + case MOUSE_CURSOR_RESIZE_ALL: cursorName = "move"; break; + case MOUSE_CURSOR_NOT_ALLOWED: cursorName = "not-allowed"; break; + case MOUSE_CURSOR_ARROW: // WARNING: It does not seem t be a specific cursor for arrow + case MOUSE_CURSOR_DEFAULT: cursorName = "default"; break; + default: + { + cursorName = "default"; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_DEFAULT; + } break; + } - default: - { - TRACELOG(LOG_WARNING, "Cursor value out of bound (%d). Setting to default", cursor); - cursorName = "default"; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_DEFAULT; - } break; + // Set the cursor element on the canvas CSS + // The canvas is coded to the Id "canvas" on init + EM_ASM({document.getElementById("canvas").style.cursor = UTF8ToString($0);}, cursorName); } - - // Set the cursor element on the canvas CSS - // The canvas is coded to the Id "canvas" on init - EM_ASM({document.getElementById("canvas").style.cursor = UTF8ToString($0);}, cursorName); } // Register all input events From 6d7112fde7ee2f3372f7cdc95a9f474328973cdc Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sat, 14 Oct 2023 22:46:46 +0200 Subject: [PATCH 0052/1037] Fix some omissions (#3418) Changes the return type of `InitGraphicsDevice()` from `bool` to `int`. Adds a return at the end of `InitPlatform()`. --- src/rcore_android.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index b7680adbc..4dc0e26c4 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -676,6 +676,8 @@ static int InitPlatform(void) //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; } } + + return 0; } // Close platform @@ -707,7 +709,7 @@ static void ClosePlatform(void) // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size // NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(void) +static int InitGraphicsDevice(void) { CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; From 781f7175308f0b708393b1135657aa191ca2b508 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 14 Oct 2023 17:47:35 -0300 Subject: [PATCH 0053/1037] Remove the rcore.h include from drm, web, template (#3420) --- src/rcore_drm.c | 22 ++++++++++------------ src/rcore_template.c | 12 +++++------- src/rcore_web.c | 18 ++++++++---------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 8f1bfa429..3f03bfa12 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -47,8 +47,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #include // POSIX file control definitions - open(), creat(), fcntl() #include // POSIX standard function definitions - read(), close(), STDIN_FILENO #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() @@ -218,12 +216,12 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - + // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- - + // Initialize rlgl default data (buffers and shaders) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -272,10 +270,10 @@ void InitWindow(int width, int height, const char *title) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif - + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -302,7 +300,7 @@ void CloseWindow(void) #endif // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -721,7 +719,7 @@ static int InitPlatform(void) platform.gbmSurface = NULL; platform.prevBO = NULL; platform.prevFB = 0; - + CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -1034,9 +1032,9 @@ static int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); @@ -1057,7 +1055,7 @@ static int InitPlatform(void) InitEvdevInput(); // Evdev inputs initialization InitGamepad(); // Gamepad init InitKeyboard(); // Keyboard init (stdin) - + return 0; } diff --git a/src/rcore_template.c b/src/rcore_template.c index 3929de4b3..89be8ac00 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -46,8 +46,6 @@ * **********************************************************************************************/ -#include "rcore.h" - // TODO: Include the platform specific libraries //---------------------------------------------------------------------------------- @@ -130,8 +128,8 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; - - + + // TODO: Platform specific init window //-------------------------------------------------------------- CORE.Window.screen.width = width; @@ -144,7 +142,7 @@ void InitWindow(int width, int height, const char *title) CORE.Window.ready = InitGraphicsDevice(width, height); - + // Initialize OpenGL context (states and resources) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -711,13 +709,13 @@ static int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); CORE.Window.ready = true; - + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } // Initialize hi-res timer InitTimer(); - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); diff --git a/src/rcore_web.c b/src/rcore_web.c index af2b6e36f..9e4ce5b97 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -45,8 +45,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) // #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management @@ -177,7 +175,7 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- @@ -256,7 +254,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -1005,7 +1003,7 @@ static int InitPlatform(void) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); - + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(glfwGetProcAddress); @@ -1029,19 +1027,19 @@ static int InitPlatform(void) TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Initialize hi-res timer InitTimer(); - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); @@ -1070,7 +1068,7 @@ static int InitPlatform(void) // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); - + return 0; } From 18bedbd0952c27b0eb8bc5df0df4acf589cef181 Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Sat, 14 Oct 2023 16:51:35 -0400 Subject: [PATCH 0054/1037] [core] Change axisCount to be an array (#3421) * Update `PLATFORM_DRM` implementation of `GetGamepadAxisCount` * Update * Update `PLATFORM_DRM` implementation of `GetGamepadName` * Add example to test gamepad info functions Fix typo * Update new gamepad info example * Move axis count update out of GamepadThread - race condition * Remove pointless if statement * Start integrating stuff from the mikesinput lib * Add more logging * Add semicolon * Add forgotten static * More fixes * Update axisCount to be array * More debugging * Add forgotten index to ready check * Add path logging * Missing parenthesis * Add missing slash * Fix axis count being reset to 0 * Fix missing paren * Test polling joystick button events * Major updates * Fix missing array index * Fix another missing array index * Update example * dumb logging * Wrong constant for ev.code handling * More dumb logging * Remove some logging * Add FPS to gamepad info example and try for max FPS * tweak * Revert example * Add fps back * Clean up after merge * Switch axisCount to be an array --- examples/core/core_input_gamepad_info.c | 20 +++++++++++++++++--- src/rcore.c | 2 +- src/rcore.h | 2 +- src/rcore_android.c | 2 +- src/rcore_desktop.c | 4 ++-- src/rcore_drm.c | 4 ++-- src/rcore_template.c | 2 +- src/rcore_web.c | 4 ++-- 8 files changed, 27 insertions(+), 13 deletions(-) diff --git a/examples/core/core_input_gamepad_info.c b/examples/core/core_input_gamepad_info.c index 84a687cd8..55f0354b5 100644 --- a/examples/core/core_input_gamepad_info.c +++ b/examples/core/core_input_gamepad_info.c @@ -41,15 +41,29 @@ int main(void) ClearBackground(RAYWHITE); - for (int i = 0; i < 4; i++) // by default rcore.h has a MAX_GAMEPADS of 4 so mimmic that here. + for (int i = 0; i < 4; i++) // by default rcore.h has a MAX_GAMEPADS of 4 so mimmic that here. { if (IsGamepadAvailable(i)) { - DrawText(TextFormat("Gamepad:\n\tName: %s\n\tAxes: %d", GetGamepadName(i), GetGamepadAxisCount(i)), 10, y, 20, BLACK); - y += 40; + DrawText(TextFormat("Gamepad name: %s", GetGamepadName(i)), 10, y, 20, BLACK); + y += 30; + DrawText(TextFormat("\tAxis count: %d", GetGamepadAxisCount(i)), 10, y, 20, BLACK); + y += 30; + for (int axis = 0; axis < GetGamepadAxisCount(i); axis++) + { + DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 20, BLACK); + y += 30; + } + for (int button = 0; button < 32; button++) + { + DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 20, BLACK); + y += 30; + } } } + DrawFPS(GetScreenWidth() - 100, 100); + EndDrawing(); } diff --git a/src/rcore.c b/src/rcore.c index e6015b33f..f94731f90 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2168,7 +2168,7 @@ int GetGamepadButtonPressed(void) // Get gamepad axis count int GetGamepadAxisCount(int gamepad) { - return CORE.Input.Gamepad.axisCount; + return CORE.Input.Gamepad.axisCount[gamepad]; } // Get axis movement vector for a gamepad diff --git a/src/rcore.h b/src/rcore.h index dbff6ab13..1127585a0 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -179,7 +179,7 @@ typedef struct CoreData { } Touch; struct { int lastButtonPressed; // Register last gamepad button pressed - int axisCount; // Register number of available gamepad axis + int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready char name[MAX_GAMEPADS][64]; // Gamepad name holder char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state diff --git a/src/rcore_android.c b/src/rcore_android.c index 4dc0e26c4..98ce64a6b 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -566,7 +566,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Register previous touch states for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index fe5a02aac..4039bbd84 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1234,7 +1234,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) // Register previous keys states @@ -1341,7 +1341,7 @@ void PollInputEvents(void) CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f); CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f); - CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1; + CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1; } } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 3f03bfa12..1fb83c8fe 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -655,7 +655,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Register previous keys states for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) @@ -1847,7 +1847,7 @@ static void InitGamepad(void) } ioctl(platform.gamepadStreamFd[i], JSIOCGNAME(64), &CORE.Input.Gamepad.name[i]); - ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount); + ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount[i]); } } } diff --git a/src/rcore_template.c b/src/rcore_template.c index 89be8ac00..4d2db9d30 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -569,7 +569,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Register previous touch states for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; diff --git a/src/rcore_web.c b/src/rcore_web.c index 9e4ce5b97..f8e1e5b2d 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -705,7 +705,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) // Register previous keys states @@ -799,7 +799,7 @@ void PollInputEvents(void) CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; } - CORE.Input.Gamepad.axisCount = gamepadState.numAxes; + CORE.Input.Gamepad.axisCount[i] = gamepadState.numAxes; } } } From a75251f0a9732f42cfeb8d018210a358f46c910d Mon Sep 17 00:00:00 2001 From: Johnathan Corkery Date: Sun, 15 Oct 2023 18:25:39 -0400 Subject: [PATCH 0055/1037] Inclusion of Matte to BINDINGS.md (#3427) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index e08f8f79e..400bc3846 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -41,6 +41,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | KaylibKit | **4.5**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/KaylibKit | | raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | +| raylib-matte | 4.6-dev | [Matte](https://github.com/jcorks/matte/) | MIT | https://github.com/jcorks/raylib-matte | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | Raylib.nelua | **4.5** | [nelua](https://nelua.io/) | Zlib | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | From 84818c96f236cc4ff172dda2013aba231f68ff13 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Oct 2023 00:51:44 +0200 Subject: [PATCH 0056/1037] ADDED: NEW PLATFORM: SDL (DESKTOP) `rcore_desktop_sdl` #3313 --- src/rcore.c | 22 +- src/rcore_desktop_sdl.c | 755 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 769 insertions(+), 8 deletions(-) create mode 100644 src/rcore_desktop_sdl.c diff --git a/src/rcore.c b/src/rcore.c index f94731f90..b813b0197 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,14 +3,18 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP: Windows (Win32, Win64) -* - PLATFORM_DESKTOP: Linux (X11 desktop mode) -* - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - PLATFORM_DESKTOP: OSX/macOS -* - PLATFORM_WEB: HTML5 (WebAssembly) -* - PLATFORM_DRM: Raspberry Pi 0-5 -* - PLATFORM_DRM: Linux native mode (KMS driver) -* - PLATFORM_ANDROID: Android (ARM, ARM64) +* - PLATFORM_DESKTOP: +* > Windows (Win32, Win64) +* > Linux (X11/Wayland desktop mode) +* > macOS/OSX (x64, arm64) +* > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - PLATFORM_WEB: +* > HTML5 (WebAssembly) +* - PLATFORM_DRM: +* > Raspberry Pi 0-5 +* > Linux native mode (KMS driver) +* - PLATFORM_ANDROID: +* > Android (ARM, ARM64) * * CONFIGURATION: * #define SUPPORT_DEFAULT_FONT (default) @@ -303,6 +307,8 @@ const char *TextFormat(const char *text, ...); // Formatting of text with // Include platform-specific submodules #if defined(PLATFORM_DESKTOP) #include "rcore_desktop.c" +#elif defined(PLATFORM_DESKTOP_SDL) + #include "rcore_desktop_sdl.c" #elif defined(PLATFORM_WEB) #include "rcore_web.c" #elif defined(PLATFORM_DRM) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c new file mode 100644 index 000000000..e85d18965 --- /dev/null +++ b/src/rcore_desktop_sdl.c @@ -0,0 +1,755 @@ +/********************************************************************************************** +* +* rcore_desktop_sdl - Functions to manage window, graphics device and inputs +* +* PLATFORM: DESKTOP: SDL +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - OSX/macOS (x64, arm64) +* +* 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: +* - SDL 2 (main library) +* - Dependency 02 +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 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 "rcore.h" + +#include "SDL.h" // SDL base library (window/rendered, input, timming... functionality) +#include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { + SDL_Window *window; + SDL_GLContext glContext; + + SDL_Joystick *gamepad; +} PlatformData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; + + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); + //-------------------------------------------------------------- + + // Initialize rlgl default data (buffers and shaders) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + #if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif +#else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes + #endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); + //-------------------------------------------------------------- + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + //SDL_SetWindowFullscreen +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + //SDL_SetWindowFullscreen +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + SDL_MaximizeWindow(platform.window); + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + SDL_MinimizeWindow(platform.window); + CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + SDL_ShowWindow(platform.window); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + //SDL_HideWindow(platform.window); +} + +// 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; +} + +// 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) +{ + SDL_GL_SwapWindow(platform.window); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// Get elapsed time measure in seconds +double GetTime(void) +{ + unsigned int ms = SDL_GetTicks(); // Elapsed time in milliseconds since SDL_Init() + double time = (double)ms/1000; + return time; +} + +// Open URL with default system browser (if available) +void OpenURL(const char *url) +{ + SDL_OpenURL(url); +} + +//---------------------------------------------------------------------------------- +// 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"); +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; + for (int i = 0; i < MAX_GAMEPADS; i++) CORE.Input.Gamepad.axisCount[i] = 0; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on target platform the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Register previous keys states + // NOTE: Android supports up to 260 keys + for (int i = 0; i < 260; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Poll input events for current plaform + //----------------------------------------------------------------------------- + /* + // WARNING: Indexes into this array are obtained by using SDL_Scancode values, not SDL_Keycode values + const Uint8 *keys = SDL_GetKeyboardState(NULL); + for (int i = 0; i < 256; ++i) + { + CORE.Input.Keyboard.currentKeyState[i] = keys[i]; + //if (keys[i]) TRACELOG(LOG_WARNING, "Pressed key: %i", i); + } + */ + + SDL_Event event = { 0 }; + while (SDL_PollEvent(&event) != 0) + { + // All input events can be processed after polling + switch (event.type) + { + case SDL_QUIT: CORE.Window.shouldClose = true; break; + + // Window events are also polled (Minimized, maximized, close...) + case SDL_WINDOWEVENT: + { + switch (event.window.event) + { + case SDL_WINDOWEVENT_LEAVE: + case SDL_WINDOWEVENT_HIDDEN: + case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_FOCUS_LOST: + case SDL_WINDOWEVENT_ENTER: + case SDL_WINDOWEVENT_SHOWN: + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_MAXIMIZED: + case SDL_WINDOWEVENT_RESTORED: + default: break; + } + } break; + + // Keyboard events + case SDL_KEYDOWN: + { + CORE.Input.Keyboard.currentKeyState[event.key.keysym.sym] = 1; + + if (event.key.keysym.sym == SDLK_ESCAPE) + { + CORE.Window.shouldClose = true; + } + } break; + case SDL_KEYUP: break; + + // Check mouse events + case SDL_MOUSEBUTTONDOWN: + { + CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 1; + } break; + case SDL_MOUSEBUTTONUP: break; + case SDL_MOUSEWHEEL: + { + CORE.Input.Mouse.currentWheelMove.x = (float)event.wheel.x; + CORE.Input.Mouse.currentWheelMove.y = (float)event.wheel.y; + } break; + case SDL_MOUSEMOTION: + { + CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; + CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; + } break; + + // Check gamepad events + case SDL_JOYAXISMOTION: + { + // Motion on gamepad 0 + if (event.jaxis.which == 0) + { + // X axis motion + if (event.jaxis.axis == 0) + { + //... + } + // Y axis motion + else if (event.jaxis.axis == 1) + { + //... + } + } + } break; + default: break; + } + } + //----------------------------------------------------------------------------- +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) +{ + // Initialize SDL internal global state + int result = SDL_Init(SDL_INIT_EVERYTHING); + if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; } + + unsigned int flags = 0; + flags |= SDL_WINDOW_SHOWN; + flags |= SDL_WINDOW_OPENGL; + flags |= SDL_WINDOW_INPUT_FOCUS; + flags |= SDL_WINDOW_MOUSE_FOCUS; + flags |= SDL_WINDOW_MOUSE_CAPTURE; // Window has mouse captured + + // Check window creation flags + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) + { + CORE.Window.fullscreen = true; + flags |= SDL_WINDOW_FULLSCREEN; + } + + //if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) flags |= SDL_WINDOW_HIDDEN; + if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= SDL_WINDOW_BORDERLESS; + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) flags |= SDL_WINDOW_RESIZABLE; + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) flags |= SDL_WINDOW_MINIMIZED; + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= SDL_WINDOW_MAXIMIZED; + + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) + { + flags &= ~SDL_WINDOW_INPUT_FOCUS; + flags &= ~SDL_WINDOW_MOUSE_FOCUS; + } + + if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) flags |= SDL_WINDOW_ALWAYS_ON_TOP; + if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) flags &= ~SDL_WINDOW_MOUSE_CAPTURE; + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) flags |= SDL_WINDOW_ALLOW_HIGHDPI; + + //if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= SDL_WINDOW_TRANSPARENT; // Alternative: SDL_GL_ALPHA_SIZE = 8 + + //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + + // NOTE: Some OpenGL context attributes must be set before window creation + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + } + + // Init window + platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags); + + // Init OpenGL context + platform.glContext = SDL_GL_CreateContext(platform.window); + + // Check window and glContext have been initialized succesfully + if ((platform.window != NULL) && (platform.glContext != NULL)) + { + CORE.Window.ready = true; + + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + else { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(SDL_GL_GetProcAddress); + + + // Init input gamepad + if (SDL_NumJoysticks() >= 1) + { + SDL_Joystick *gamepad = SDL_JoystickOpen(0); + //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); + } + + // Initialize hi-res timer + //InitTimer(); + CORE.Time.previous = GetTime(); // Get time as double + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + return 0; +} + +static void ClosePlatform(void) +{ + SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context + SDL_DestroyWindow(platform.window); + SDL_Quit(); // Deinitialize SDL internal global state +} +// EOF From 73363f829b709dddedf21a5e6b696f37e44bce19 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 16 Oct 2023 04:43:20 -0300 Subject: [PATCH 0057/1037] [core] Fix some mouse issues on `SDL` (#3428) * Fix mouse wheel getting stucked scrolling up or down * Fix mouse movement on 3D * Fix mouse button presses --- src/rcore_desktop_sdl.c | 57 +++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index e85d18965..4ce09cf80 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -60,7 +60,7 @@ typedef struct { SDL_Window *window; SDL_GLContext glContext; - + SDL_Joystick *gamepad; } PlatformData; @@ -134,12 +134,12 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - + // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- - + // Initialize rlgl default data (buffers and shaders) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -214,7 +214,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -481,7 +481,7 @@ void SwapScreenBuffer(void) double GetTime(void) { unsigned int ms = SDL_GetTicks(); // Elapsed time in milliseconds since SDL_Init() - double time = (double)ms/1000; + double time = (double)ms/1000; return time; } @@ -531,6 +531,13 @@ void PollInputEvents(void) // Reset key repeats for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + // Reset mouse wheel + CORE.Input.Mouse.currentWheelMove.x = 0; + CORE.Input.Mouse.currentWheelMove.y = 0; + + // Register previous mouse position + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; for (int i = 0; i < MAX_GAMEPADS; i++) CORE.Input.Gamepad.axisCount[i] = 0; @@ -551,6 +558,9 @@ void PollInputEvents(void) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; } + // Register previous mouse states + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + // Poll input events for current plaform //----------------------------------------------------------------------------- /* @@ -602,12 +612,15 @@ void PollInputEvents(void) case SDL_KEYUP: break; // Check mouse events - case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONDOWN: { CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 1; } break; - case SDL_MOUSEBUTTONUP: break; - case SDL_MOUSEWHEEL: + case SDL_MOUSEBUTTONUP: + { + CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 0; + } break; + case SDL_MOUSEWHEEL: { CORE.Input.Mouse.currentWheelMove.x = (float)event.wheel.x; CORE.Input.Mouse.currentWheelMove.y = (float)event.wheel.y; @@ -653,7 +666,7 @@ static int InitPlatform(void) // Initialize SDL internal global state int result = SDL_Init(SDL_INIT_EVERYTHING); if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; } - + unsigned int flags = 0; flags |= SDL_WINDOW_SHOWN; flags |= SDL_WINDOW_OPENGL; @@ -662,12 +675,12 @@ static int InitPlatform(void) flags |= SDL_WINDOW_MOUSE_CAPTURE; // Window has mouse captured // Check window creation flags - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) { CORE.Window.fullscreen = true; flags |= SDL_WINDOW_FULLSCREEN; } - + //if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) flags |= SDL_WINDOW_HIDDEN; if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= SDL_WINDOW_BORDERLESS; if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) flags |= SDL_WINDOW_RESIZABLE; @@ -684,11 +697,11 @@ static int InitPlatform(void) if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) flags &= ~SDL_WINDOW_MOUSE_CAPTURE; if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) flags |= SDL_WINDOW_ALLOW_HIGHDPI; - + //if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= SDL_WINDOW_TRANSPARENT; // Alternative: SDL_GL_ALPHA_SIZE = 8 - + //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - + // NOTE: Some OpenGL context attributes must be set before window creation SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); @@ -702,12 +715,12 @@ static int InitPlatform(void) // Init window platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags); - + // Init OpenGL context platform.glContext = SDL_GL_CreateContext(platform.window); - + // Check window and glContext have been initialized succesfully - if ((platform.window != NULL) && (platform.glContext != NULL)) + if ((platform.window != NULL) && (platform.glContext != NULL)) { CORE.Window.ready = true; @@ -728,21 +741,21 @@ static int InitPlatform(void) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(SDL_GL_GetProcAddress); - + // Init input gamepad if (SDL_NumJoysticks() >= 1) { SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } - + // Initialize hi-res timer //InitTimer(); CORE.Time.previous = GetTime(); // Get time as double - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + return 0; } From c4296b166af5f1859707234dddae73d4dce84628 Mon Sep 17 00:00:00 2001 From: neyrox Date: Mon, 16 Oct 2023 15:06:12 +0300 Subject: [PATCH 0058/1037] Fix GenMeshPlane when resX != resZ (#3425) Co-authored-by: Stanislav Yablonskiy --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index a11ecf799..f9018eaaf 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2249,7 +2249,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) for (int face = 0; face < numFaces; face++) { // Retrieve lower left corner from face ind - int i = face % (resX - 1) + (face/(resZ - 1)*resX); + int i = face + face / (resX - 1); triangles[t++] = i + resX; triangles[t++] = i + 1; From 859c67792a839021b98b7abf25a664b7109cff3f Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Mon, 16 Oct 2023 13:08:55 +0100 Subject: [PATCH 0059/1037] Make sure rcore.o gets compiled in more situations (#3423) Currently doing the following: ``` make touch rcore_desktop.c make ``` Will not result in rcore.o getting compiled again, despite that rcore_desktop.c has changed This commit resolves that --- src/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile b/src/Makefile index e75ae5368..56b6d0632 100644 --- a/src/Makefile +++ b/src/Makefile @@ -632,6 +632,9 @@ endif # Compile all modules with their prerequisites +# Prerequisites of core module +rcore.o : rcore_android.c rcore_desktop.c rcore_drm.c rcore_template.c rcore_web.c + # Compile core module rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) From fab99b8309b483f81c8a7b2524da0e0e0079560f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 16 Oct 2023 09:59:08 -0300 Subject: [PATCH 0060/1037] Remove rcore.h include from android (#3429) --- src/rcore_android.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index 98ce64a6b..cfc8edf61 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -46,8 +46,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #include // Required for: android_app struct and activity management #include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others //#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) @@ -188,7 +186,7 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- } @@ -216,7 +214,7 @@ void CloseWindow(void) #endif // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -869,7 +867,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Initialize graphics device (display device and OpenGL context) InitGraphicsDevice(); - + // Initialize OpenGL context (states and resources) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -908,7 +906,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes #endif #endif - + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); From af83764f4fe83d9f036fab94a6ab886ffdf82587 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:53:53 +0300 Subject: [PATCH 0061/1037] Implement GetCurrentMonitor in rcore_desktop_sdl (#3431) * Implemented GetCurrentMonitor * remove traceloog in GetCurrentMonitor --- src/rcore_desktop_sdl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 4ce09cf80..7f593abcb 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -356,8 +356,7 @@ int GetMonitorCount(void) // Get number of monitors int GetCurrentMonitor(void) { - TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); - return 0; + return SDL_GetWindowDisplayIndex(platform.window); } // Get selected monitor position From 7290ea9bfbccd62ab5e4870ca84868c2aecf5382 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Oct 2023 10:59:25 +0200 Subject: [PATCH 0062/1037] Update models_mesh_generation.c --- examples/models/models_mesh_generation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index d17a20a12..94e0a4c48 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -36,7 +36,7 @@ int main(void) Model models[NUM_MODELS] = { 0 }; - models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 5, 5)); + models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 4, 3)); models[1] = LoadModelFromMesh(GenMeshCube(2.0f, 1.0f, 2.0f)); models[2] = LoadModelFromMesh(GenMeshSphere(2, 32, 32)); models[3] = LoadModelFromMesh(GenMeshHemiSphere(2, 16, 16)); From 99ede0f747f6d8a0b0f7d0d9371b9d46af16cb80 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Oct 2023 11:09:56 +0200 Subject: [PATCH 0063/1037] Added some notes for alternative implementations #3362 --- src/rtext.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 2d72bbe6b..5b43bfb92 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1344,10 +1344,12 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint) // Get text length in bytes, check for \0 character unsigned int TextLength(const char *text) { - unsigned int length = 0; //strlen(text) + unsigned int length = 0; if (text != NULL) { + // NOTE: Alternative: use strlen(text) + while (*text++) length++; } @@ -1415,6 +1417,8 @@ int TextCopy(char *dst, const char *src) if ((src != NULL) && (dst != NULL)) { + // NOTE: Alternative: use strcpy(dst, src) + while (*src != '\0') { *dst = *src; @@ -1459,6 +1463,8 @@ const char *TextSubtext(const char *text, int position, int length) } if (length >= textLength) length = textLength; + + // NOTE: Alternative: memcpy(buffer, text + position, length) for (int c = 0 ; c < length ; c++) { From f353cd1c3a884d6b80af311d40586ad414e09efd Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 17 Oct 2023 07:01:01 -0300 Subject: [PATCH 0064/1037] [core] Add some missing implementations to `SDL` (#3432) * Add missing implementations * Add missing implementations 2 * Add missing implementations 3 * Add missing implementations 4 * Add missing implementations 5 --- src/rcore_desktop_sdl.c | 87 ++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 14 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 7f593abcb..0910343db 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -292,13 +292,18 @@ void SetWindowIcons(Image *images, int count) // Set title for window void SetWindowTitle(const char *title) { + SDL_SetWindowTitle(platform.window, 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"); + SDL_SetWindowPosition(platform.window, x, y); + + CORE.Window.position.x = x; + CORE.Window.position.y = y; } // Set monitor for the current window @@ -324,13 +329,19 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform"); + SDL_SetWindowSize(platform.window, width, height); + + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; } // 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"); + if (opacity >= 1.0f) opacity = 1.0f; + else if (opacity <= 0.0f) opacity = 0.0f; + + SDL_SetWindowOpacity(platform.window, opacity); } // Set window focused @@ -349,8 +360,11 @@ void *GetWindowHandle(void) // Get number of monitors int GetMonitorCount(void) { - TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); - return 1; + int monitorCount = 0; + + monitorCount = SDL_GetNumVideoDisplays(); + + return monitorCount; } // Get number of monitors @@ -369,15 +383,39 @@ Vector2 GetMonitorPosition(int monitor) // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); - return 0; + int width = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + width = mode.w; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return width; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); - return 0; + int height = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + height = mode.h; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return height; } // Get selected monitor physical width in millimetres @@ -397,22 +435,43 @@ int GetMonitorPhysicalHeight(int monitor) // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); - return 0; + int refresh = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + refresh = mode.refresh_rate; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return refresh; } // 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"); + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) return SDL_GetDisplayName(monitor); + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + return ""; } // Get window position XY on monitor Vector2 GetWindowPosition(void) { - TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform"); - return (Vector2){ 0, 0 }; + int x = 0; + int y = 0; + + SDL_GetWindowPosition(platform.window, &x, &y); + + return (Vector2){ (float)x, (float)y }; } // Get window scale DPI factor for current monitor From 80432fde62a31ff5e7ad61f8a0352de9642cd97f Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Tue, 17 Oct 2023 23:29:28 +0200 Subject: [PATCH 0065/1037] Fix SDL keyboard issue (#3435) * Fix SDL keyboard issue We have added a mapping table between raylib keys and SDL scancodes. * Change `ScancodeToKey` array type --- src/rcore_desktop_sdl.c | 134 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 5 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 0910343db..b66167c89 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -71,11 +71,119 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data +//---------------------------------------------------------------------------------- +// Local Variables Definition +//---------------------------------------------------------------------------------- +#define SCANCODE_MAPPED_NUM 100 +static const KeyboardKey ScancodeToKey[SCANCODE_MAPPED_NUM] = { + KEY_NULL, // SDL_SCANCODE_UNKNOWN + 0, + 0, + 0, + KEY_A, // SDL_SCANCODE_A + KEY_B, // SDL_SCANCODE_B + KEY_C, // SDL_SCANCODE_C + KEY_D, // SDL_SCANCODE_D + KEY_E, // SDL_SCANCODE_E + KEY_F, // SDL_SCANCODE_F + KEY_G, // SDL_SCANCODE_G + KEY_H, // SDL_SCANCODE_H + KEY_I, // SDL_SCANCODE_I + KEY_J, // SDL_SCANCODE_J + KEY_K, // SDL_SCANCODE_K + KEY_L, // SDL_SCANCODE_L + KEY_M, // SDL_SCANCODE_M + KEY_N, // SDL_SCANCODE_N + KEY_O, // SDL_SCANCODE_O + KEY_P, // SDL_SCANCODE_P + KEY_Q, // SDL_SCANCODE_Q + KEY_R, // SDL_SCANCODE_R + KEY_S, // SDL_SCANCODE_S + KEY_T, // SDL_SCANCODE_T + KEY_U, // SDL_SCANCODE_U + KEY_V, // SDL_SCANCODE_V + KEY_W, // SDL_SCANCODE_W + KEY_X, // SDL_SCANCODE_X + KEY_Y, // SDL_SCANCODE_Y + KEY_Z, // SDL_SCANCODE_Z + KEY_ONE, // SDL_SCANCODE_1 + KEY_TWO, // SDL_SCANCODE_2 + KEY_THREE, // SDL_SCANCODE_3 + KEY_FOUR, // SDL_SCANCODE_4 + KEY_FIVE, // SDL_SCANCODE_5 + KEY_SIX, // SDL_SCANCODE_6 + KEY_SEVEN, // SDL_SCANCODE_7 + KEY_EIGHT, // SDL_SCANCODE_8 + KEY_NINE, // SDL_SCANCODE_9 + KEY_ZERO, // SDL_SCANCODE_0 + KEY_ENTER, // SDL_SCANCODE_RETURN + KEY_ESCAPE, // SDL_SCANCODE_ESCAPE + KEY_BACKSPACE, // SDL_SCANCODE_BACKSPACE + KEY_TAB, // SDL_SCANCODE_TAB + KEY_SPACE, // SDL_SCANCODE_SPACE + KEY_MINUS, // SDL_SCANCODE_MINUS + KEY_EQUAL, // SDL_SCANCODE_EQUALS + KEY_LEFT_BRACKET, // SDL_SCANCODE_LEFTBRACKET + KEY_RIGHT_BRACKET, // SDL_SCANCODE_RIGHTBRACKET + KEY_BACKSLASH, // SDL_SCANCODE_BACKSLASH + 0, // SDL_SCANCODE_NONUSHASH + KEY_SEMICOLON, // SDL_SCANCODE_SEMICOLON + KEY_APOSTROPHE, // SDL_SCANCODE_APOSTROPHE + KEY_GRAVE, // SDL_SCANCODE_GRAVE + KEY_COMMA, // SDL_SCANCODE_COMMA + KEY_PERIOD, // SDL_SCANCODE_PERIOD + KEY_SLASH, // SDL_SCANCODE_SLASH + KEY_CAPS_LOCK, // SDL_SCANCODE_CAPSLOCK + KEY_F1, // SDL_SCANCODE_F1 + KEY_F2, // SDL_SCANCODE_F2 + KEY_F3, // SDL_SCANCODE_F3 + KEY_F4, // SDL_SCANCODE_F4 + KEY_F5, // SDL_SCANCODE_F5 + KEY_F6, // SDL_SCANCODE_F6 + KEY_F7, // SDL_SCANCODE_F7 + KEY_F8, // SDL_SCANCODE_F8 + KEY_F9, // SDL_SCANCODE_F9 + KEY_F10, // SDL_SCANCODE_F10 + KEY_F11, // SDL_SCANCODE_F11 + KEY_F12, // SDL_SCANCODE_F12 + KEY_PRINT_SCREEN, // SDL_SCANCODE_PRINTSCREEN + KEY_SCROLL_LOCK, // SDL_SCANCODE_SCROLLLOCK + KEY_PAUSE, // SDL_SCANCODE_PAUSE + KEY_INSERT, // SDL_SCANCODE_INSERT + KEY_HOME, // SDL_SCANCODE_HOME + KEY_PAGE_UP, // SDL_SCANCODE_PAGEUP + KEY_DELETE, // SDL_SCANCODE_DELETE + KEY_END, // SDL_SCANCODE_END + KEY_PAGE_DOWN, // SDL_SCANCODE_PAGEDOWN + KEY_RIGHT, // SDL_SCANCODE_RIGHT + KEY_LEFT, // SDL_SCANCODE_LEFT + KEY_DOWN, // SDL_SCANCODE_DOWN + KEY_UP, // SDL_SCANCODE_UP + KEY_NUM_LOCK, // SDL_SCANCODE_NUMLOCKCLEAR + KEY_KP_DIVIDE, // SDL_SCANCODE_KP_DIVIDE + KEY_KP_MULTIPLY, // SDL_SCANCODE_KP_MULTIPLY + KEY_KP_SUBTRACT, // SDL_SCANCODE_KP_MINUS + KEY_KP_ADD, // SDL_SCANCODE_KP_PLUS + KEY_KP_ENTER, // SDL_SCANCODE_KP_ENTER + KEY_KP_1, // SDL_SCANCODE_KP_1 + KEY_KP_2, // SDL_SCANCODE_KP_2 + KEY_KP_3, // SDL_SCANCODE_KP_3 + KEY_KP_4, // SDL_SCANCODE_KP_4 + KEY_KP_5, // SDL_SCANCODE_KP_5 + KEY_KP_6, // SDL_SCANCODE_KP_6 + KEY_KP_7, // SDL_SCANCODE_KP_7 + KEY_KP_8, // SDL_SCANCODE_KP_8 + KEY_KP_9, // SDL_SCANCODE_KP_9 + KEY_KP_0, // SDL_SCANCODE_KP_0 + KEY_KP_DECIMAL // SDL_SCANCODE_KP_PERIOD +}; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform +static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help convert SDL scancodes to raylib key //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -660,14 +768,21 @@ void PollInputEvents(void) // Keyboard events case SDL_KEYDOWN: { - CORE.Input.Keyboard.currentKeyState[event.key.keysym.sym] = 1; + KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); + if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 1; - if (event.key.keysym.sym == SDLK_ESCAPE) + // TODO: Put exitKey verification outside the switch? + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) { CORE.Window.shouldClose = true; } } break; - case SDL_KEYUP: break; + + case SDL_KEYUP: + { + KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); + if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0; + } break; // Check mouse events case SDL_MOUSEBUTTONDOWN: @@ -823,4 +938,13 @@ static void ClosePlatform(void) SDL_DestroyWindow(platform.window); SDL_Quit(); // Deinitialize SDL internal global state } + +static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) +{ + if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) + { + return ScancodeToKey[sdlScancode]; + } + return KEY_NULL; // No equivalent key in Raylib +} // EOF From d7d04a07a28a80569f440459f2a1ca4ea2fe2497 Mon Sep 17 00:00:00 2001 From: Alexey Kutepov Date: Wed, 18 Oct 2023 04:35:38 +0700 Subject: [PATCH 0066/1037] [raudio] Implement GetMasterVolume() (#3434) It feels a little unfinished when you can SetMasterVolume but can't really Get it. So to finish the symmetry here is the GetMasterVolume implementation. --- src/raudio.c | 8 ++++++++ src/raylib.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index a8d1b40e2..6a1096767 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -536,6 +536,14 @@ void SetMasterVolume(float volume) ma_device_set_master_volume(&AUDIO.System.device, volume); } +// Get master volume (listener) +float GetMasterVolume(void) +{ + float volume = 0.0f; + ma_device_get_master_volume(&AUDIO.System.device, &volume); + return volume; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Buffer management //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index c03e0a576..331bf5253 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1538,6 +1538,7 @@ RLAPI void InitAudioDevice(void); // Initial RLAPI void CloseAudioDevice(void); // Close the audio device and context RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully RLAPI void SetMasterVolume(float volume); // Set master volume (listener) +RLAPI float GetMasterVolume(void); // Get master volume (listener) // Wave/Sound loading/unloading functions RLAPI Wave LoadWave(const char *fileName); // Load wave data from file From 9534f48425bb87e205406fe950b606a2186ccaac Mon Sep 17 00:00:00 2001 From: Michael Scherbakow Date: Tue, 17 Oct 2023 23:36:42 +0200 Subject: [PATCH 0067/1037] fix build.zig (#3433) for zig master (2023-10-17) --- src/build.zig | 83 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/src/build.zig b/src/build.zig index 5d7ddbf3f..5e2b5916e 100644 --- a/src/build.zig +++ b/src/build.zig @@ -20,37 +20,55 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } - raylib.addCSourceFiles(&.{ - srcdir ++ "/rcore.c", - srcdir ++ "/utils.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rcore.c", + srcdir ++ "/utils.c", + }, + .flags = raylib_flags, + }); if (options.raudio) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/raudio.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/raudio.c", + }, + .flags = raylib_flags, + }); } if (options.rmodels) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rmodels.c", - }, &[_][]const u8{ - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 - } ++ raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rmodels.c", + }, + .flags = &[_][]const u8{ + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 + } ++ raylib_flags, + }); } if (options.rshapes) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rshapes.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rshapes.c", + }, + .flags = raylib_flags, + }); } if (options.rtext) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rtext.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rtext.c", + }, + .flags = raylib_flags, + }); } if (options.rtextures) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rtextures.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rtextures.c", + }, + .flags = raylib_flags, + }); } var gen_step = b.addWriteFiles(); @@ -65,7 +83,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built switch (target.getOsTag()) { .windows => { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags, + }); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -75,7 +96,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, .linux => { if (!options.platform_drm) { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags, + }); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -103,7 +127,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags, + }); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -122,10 +149,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const raylib_flags_extra_macos = &[_][]const u8{ "-ObjC", }; - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags ++ raylib_flags_extra_macos, - ); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags ++ raylib_flags_extra_macos, + }); raylib.linkFramework("Foundation"); raylib.linkFramework("CoreServices"); raylib.linkFramework("CoreGraphics"); From 53cd60bb29c97951cd0379aa230d43f794f055ce Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Oct 2023 00:03:47 +0200 Subject: [PATCH 0068/1037] REVIEWED: Move `InitWindow()`/`CloseWindow()` to `rcore.c` #3313 --- src/rcore.c | 143 ++++++++++++++++++++++++++++++++++ src/rcore_android.c | 90 --------------------- src/rcore_desktop.c | 161 +++----------------------------------- src/rcore_desktop_sdl.c | 140 --------------------------------- src/rcore_drm.c | 144 ---------------------------------- src/rcore_template.c | 169 +--------------------------------------- src/rcore_web.c | 141 --------------------------------- 7 files changed, 153 insertions(+), 835 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b813b0197..c6770a5bb 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -281,6 +281,9 @@ extern void LoadFontDefault(void); // [Module: text] Loads default font on extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform + static void InitTimer(void); // Initialize timer (hi-resolution if available) static void SetupFramebuffer(int width, int height); // Setup main framebuffer static void SetupViewport(int width, int height); // Set viewport for a provided width and height @@ -369,6 +372,146 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void EnableCursor(void) //void DisableCursor(void) +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; + + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); + //-------------------------------------------------------------- + + // Initialize rlgl default data (buffers and shaders) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + #if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif +#else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes + #endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); + //-------------------------------------------------------------- + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + // Check if window has been initialized successfully bool IsWindowReady(void) { diff --git a/src/rcore_android.c b/src/rcore_android.c index cfc8edf61..bf55ece27 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -136,96 +136,6 @@ struct android_app *GetAndroidApp(void) // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 4039bbd84..29969d4ca 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -142,162 +142,11 @@ static void JoystickCallback(int jid, int event); // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- - - // Initialize rlgl default data (buffers and shaders) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); - } - #endif -#else - #if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes - #endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close // NOTE: By default, if KEY_ESCAPE pressed or window close icon clicked bool WindowShouldClose(void) { - if (CORE.Window.ready) - { - // While window minimized, stop loop execution - while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); - - CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle); - - // Reset close status for next frame - glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); - - return CORE.Window.shouldClose; - } + if (CORE.Window.ready) return CORE.Window.shouldClose; else return true; } @@ -1349,6 +1198,14 @@ void PollInputEvents(void) if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) + + // While window minimized, stop loop execution + while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); + + CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle); + + // Reset close status for next frame + glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); } diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index b66167c89..15c79cb87 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -194,146 +194,6 @@ static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help conv // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- - - // Initialize rlgl default data (buffers and shaders) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); - } - #endif -#else - #if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes - #endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 1fb83c8fe..3302ec362 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -168,150 +168,6 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- - - // Initialize rlgl default data (buffers and shaders) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); -#if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 2, rec.y + 2, 1, 1}); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2}); - } -#endif -#else -#if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = {rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8}; - SetShapesTexture(texture, (Rectangle){0.0f, 0.0f, 1.0f, 1.0f}); // WARNING: Module required: rshapes -#endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close // NOTE: By default, if KEY_ESCAPE pressed bool WindowShouldClose(void) diff --git a/src/rcore_template.c b/src/rcore_template.c index 4d2db9d30..f94cca8c1 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -83,153 +83,6 @@ static bool InitGraphicsDevice(void); // Initialize graphics device // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // NOTE: Keep internal pointer to input title string (no copy) - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; - - - // TODO: Platform specific init window - //-------------------------------------------------------------- - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - - // Initialize graphics device - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); - - - - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); - } - #endif -#else - #if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes - #endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - - // Platform specific close window - //-------------------------------------------------------------- - // TODO. - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { @@ -503,27 +356,7 @@ void OpenURL(const char *url) if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { - JNIEnv *env = NULL; - JavaVM *vm = platform.app->activity->vm; - (*vm)->AttachCurrentThread(vm, &env, NULL); - - jstring urlString = (*env)->NewStringUTF(env, url); - jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); - jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); - jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); - - jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); - jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); - jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); - jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); - jobject intent = (*env)->AllocObject(env, intentClass); - - (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); - jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); - jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); - (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent); - - (*vm)->DetachCurrentThread(vm); + // TODO: } } diff --git a/src/rcore_web.c b/src/rcore_web.c index f8e1e5b2d..465bb8957 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -125,147 +125,6 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- - - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); -#if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 2, rec.y + 2, 1, 1}); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2}); - } -#endif -#else -#if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = {rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8}; - SetShapesTexture(texture, (Rectangle){0.0f, 0.0f, 1.0f, 1.0f}); // WARNING: Module required: rshapes -#endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { From fc6152613f4bb97708935a9f8096d2ca4d9fbb76 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Oct 2023 00:33:05 +0200 Subject: [PATCH 0069/1037] REVIEWED: `raylib 5.0-dev` version for a future release --- examples/Makefile | 2 +- examples/Makefile.Web | 2 +- src/Makefile | 4 ++-- src/raylib.dll.rc | 8 ++++---- src/raylib.dll.rc.data | Bin 11246 -> 11246 bytes src/raylib.h | 6 +++--- src/raylib.rc | 8 ++++---- src/raylib.rc.data | Bin 11182 -> 11182 bytes 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 6031f05e9..74b3b874d 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,7 +30,7 @@ PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 4.5.0 +RAYLIB_VERSION ?= 5.0.0 RAYLIB_PATH ?= .. # Locations of raylib.h and libraylib.a/libraylib.so diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 267e02396..ac8d5af89 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -30,7 +30,7 @@ PLATFORM ?= PLATFORM_WEB # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 4.5.0 +RAYLIB_VERSION ?= 5.0.0 RAYLIB_PATH ?= .. # Locations of raylib.h and libraylib.a/libraylib.so diff --git a/src/Makefile b/src/Makefile index 56b6d0632..e6c5106cc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -44,8 +44,8 @@ PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables -RAYLIB_VERSION = 4.5.0 -RAYLIB_API_VERSION = 450 +RAYLIB_VERSION = 5.0.0 +RAYLIB_API_VERSION = 500 # Define raylib source code path RAYLIB_SRC_PATH ?= ../src diff --git a/src/raylib.dll.rc b/src/raylib.dll.rc index c2a42dca2..e1455af7a 100644 --- a/src/raylib.dll.rc +++ b/src/raylib.dll.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 4,5,0,0 -PRODUCTVERSION 4,5,0,0 +FILEVERSION 5,0,0,0 +PRODUCTVERSION 5,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib dynamic library (www.raylib.com)" - VALUE "FileVersion", "4.5.0" + VALUE "FileVersion", "5.0.0" VALUE "InternalName", "raylib.dll" VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" VALUE "OriginalFilename", "raylib.dll" VALUE "ProductName", "raylib" - VALUE "ProductVersion", "4.5.0" + VALUE "ProductVersion", "5.0.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.dll.rc.data b/src/raylib.dll.rc.data index 7403bd020b0658ed1c8ee569850e2eff9a0b3a50..e93edcffbcb1e7363078ec6d16a9c83b79e50b3c 100644 GIT binary patch delta 51 zcmaDC{w{pO9Sv><237_LV4eI?Lwu4Z$7D9mYpkXWdJG1eA8I-?LYR{^wT}V-aI6ha delta 51 zcmaDC{w{pO9Sv?)1{MYo0Me5`YKTwL // Required for: va_list - Only used by TraceLogCallback -#define RAYLIB_VERSION_MAJOR 4 -#define RAYLIB_VERSION_MINOR 6 +#define RAYLIB_VERSION_MAJOR 5 +#define RAYLIB_VERSION_MINOR 0 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "4.6-dev" +#define RAYLIB_VERSION "5.0-dev" // Function specifiers in case library is build/used as a shared library (Windows) // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll diff --git a/src/raylib.rc b/src/raylib.rc index 6a9546537..8612a9561 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 4,5,0,0 -PRODUCTVERSION 4,5,0,0 +FILEVERSION 5,0,0,0 +PRODUCTVERSION 5,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib application (www.raylib.com)" - VALUE "FileVersion", "4.5.0" + VALUE "FileVersion", "5.0.0" VALUE "InternalName", "raylib app" VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" //VALUE "OriginalFilename", "raylib_app.exe" VALUE "ProductName", "raylib app" - VALUE "ProductVersion", "4.5.0" + VALUE "ProductVersion", "5.0.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.rc.data b/src/raylib.rc.data index 1485d3aa4d9470bc98c786e0699c49dd0c9c3899..1476a1cbaeb46b6b4ddc19647cb7fe87993a5e97 100644 GIT binary patch delta 51 zcmZ1%zAk*j9Sv><237_LV4eI?Lwu4Z$7D9mbF8KedJG1eZ)$QgLYR|ZY8?dtRw@lf delta 51 zcmZ1%zAk*j9Sv?)1{MYo0Me5`YKTwL Date: Wed, 18 Oct 2023 03:05:35 -0300 Subject: [PATCH 0070/1037] [core] Add more missing implementations to `SDL` (#3436) * Add more missing implementations 1 * Add more missing implementations 2 * Add more missing implementations 3 * Add more missing implementations 4 * Add more missing implementations 5 * Add more missing implementations 6 --- src/rcore_desktop_sdl.c | 87 ++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 15c79cb87..cddee7448 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -62,6 +62,7 @@ typedef struct { SDL_GLContext glContext; SDL_Joystick *gamepad; + SDL_Cursor *cursor; } PlatformData; //---------------------------------------------------------------------------------- @@ -178,6 +179,22 @@ static const KeyboardKey ScancodeToKey[SCANCODE_MAPPED_NUM] = { KEY_KP_DECIMAL // SDL_SCANCODE_KP_PERIOD }; +static const int CursorsLUT[] = { + SDL_SYSTEM_CURSOR_ARROW, // 0 MOUSE_CURSOR_DEFAULT + SDL_SYSTEM_CURSOR_ARROW, // 1 MOUSE_CURSOR_ARROW + SDL_SYSTEM_CURSOR_IBEAM, // 2 MOUSE_CURSOR_IBEAM + SDL_SYSTEM_CURSOR_CROSSHAIR, // 3 MOUSE_CURSOR_CROSSHAIR + SDL_SYSTEM_CURSOR_HAND, // 4 MOUSE_CURSOR_POINTING_HAND + SDL_SYSTEM_CURSOR_SIZEWE, // 5 MOUSE_CURSOR_RESIZE_EW + SDL_SYSTEM_CURSOR_SIZENS, // 6 MOUSE_CURSOR_RESIZE_NS + SDL_SYSTEM_CURSOR_SIZENWSE, // 7 MOUSE_CURSOR_RESIZE_NWSE + SDL_SYSTEM_CURSOR_SIZENESW, // 8 MOUSE_CURSOR_RESIZE_NESW + SDL_SYSTEM_CURSOR_SIZEALL, // 9 MOUSE_CURSOR_RESIZE_ALL + SDL_SYSTEM_CURSOR_NO // 10 MOUSE_CURSOR_NOT_ALLOWED + //SDL_SYSTEM_CURSOR_WAIT, // No equivalent implemented on MouseCursor enum on raylib.h + //SDL_SYSTEM_CURSOR_WAITARROW, // No equivalent implemented on MouseCursor enum on raylib.h +}; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -315,14 +332,13 @@ void SetWindowOpacity(float opacity) // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); + SDL_RaiseWindow(platform.window); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); - return NULL; + return (void *)platform.window; } // Get number of monitors @@ -389,15 +405,45 @@ int GetMonitorHeight(int monitor) // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); - return 0; + int width = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + float vdpi = 0.0f; + SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + // Calculate size on inches, then convert to millimeter + if (vdpi > 0.0f) width = (mode.w/vdpi)*25.4f; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return width; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); - return 0; + int height = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + float vdpi = 0.0f; + SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + // Calculate size on inches, then convert to millimeter + if (vdpi > 0.0f) height = (mode.h/vdpi)*25.4f; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return height; } // Get selected monitor refresh rate @@ -452,34 +498,37 @@ Vector2 GetWindowScaleDPI(void) // Set clipboard text content void SetClipboardText(const char *text) { - TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform"); + SDL_SetClipboardText(text); } // Get clipboard text content -// NOTE: returned string is allocated and freed by GLFW +// NOTE: returned string must be freed with SDL_free() const char *GetClipboardText(void) { - TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); - return NULL; + return SDL_GetClipboardText(); } // Show mouse cursor void ShowCursor(void) { + SDL_ShowCursor(SDL_ENABLE); + CORE.Input.Mouse.cursorHidden = false; } // Hides mouse cursor void HideCursor(void) { + SDL_ShowCursor(SDL_DISABLE); + 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); + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_ShowCursor(SDL_ENABLE); CORE.Input.Mouse.cursorHidden = false; } @@ -487,8 +536,7 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { - // Set cursor position in the middle - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + SDL_SetRelativeMouseMode(SDL_TRUE); CORE.Input.Mouse.cursorHidden = true; } @@ -524,8 +572,7 @@ void OpenURL(const char *url) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); - return 0; + SDL_GameControllerAddMapping(mappings); } // Set mouse position XY @@ -538,7 +585,10 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); + platform.cursor = SDL_CreateSystemCursor(CursorsLUT[cursor]); + SDL_SetCursor(platform.cursor); + + CORE.Input.Mouse.cursor = cursor; } // Register all input events @@ -794,6 +844,7 @@ static int InitPlatform(void) static void ClosePlatform(void) { + SDL_FreeCursor(platform.cursor); // Free cursor SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context SDL_DestroyWindow(platform.window); SDL_Quit(); // Deinitialize SDL internal global state From d7a098ebd31c3235711a473b0894bfa3e6ec0fc9 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Thu, 19 Oct 2023 00:09:00 +0200 Subject: [PATCH 0071/1037] [core] Add more missing implementations to SDL (#3439) * [core] Add more missing implementations to SDL Add functions: `SetWindowState`, `ClearWindowState`, `SetWindowIcon` * Completing `SetWIndowState` and `ClearWindowState` * Add VSync support for SDL * Fix `CORE.Window.display` size issue * Fix getting monitor size We now get the size of the monitor where the window is located * Add `ToggleBorderlessWindowed` * Add `ToggleFullscreen` * Add `GetMonitorPosition` * Add `SetWindowMonitor` NOTE: The function is implemented but incomplete * Replace `TraceLog` by `TRACELOG` * Fixed mouse delta issue in relative mode Fixed a delta retrieval issue with `GetMouseDelta` when the mouse is in relative mode. Solution by @ubkp * Fix `IsKeyPressed` issue An issue caused `IsKeyPressed` to continuously return true for most keys when pressed * Fix `SetGamepadMappings` returning --- src/rcore_desktop_sdl.c | 383 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 360 insertions(+), 23 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index cddee7448..fedebeee4 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -63,6 +63,7 @@ typedef struct { SDL_Joystick *gamepad; SDL_Cursor *cursor; + bool cursorRelative; } PlatformData; //---------------------------------------------------------------------------------- @@ -221,13 +222,72 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - //SDL_SetWindowFullscreen + if (!IsWindowState(FLAG_FULLSCREEN_MODE)) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + } + else + { + SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + } } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - //SDL_SetWindowFullscreen + // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it + bool wasOnFullscreen = false; + if (CORE.Window.fullscreen) + { + CORE.Window.previousPosition = CORE.Window.position; + ToggleFullscreen(); + wasOnFullscreen = true; + } + + if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) + { + // Store the window's current position and size + SDL_GetWindowPosition(platform.window, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + CORE.Window.previousScreen = CORE.Window.screen; + + // Set screen position and size inside valid bounds + SDL_Rect displayBounds; + if (SDL_GetDisplayBounds(GetCurrentMonitor(), &displayBounds) == 0) + { + SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); + SDL_SetWindowSize(platform.window, displayBounds.w, displayBounds.h); + } + + // Set borderless mode and flag + SDL_SetWindowBordered(platform.window, SDL_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + + // Set topmost modes and flag + SDL_SetWindowAlwaysOnTop(platform.window, SDL_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + + // Set borderless windowed flag + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + // Remove borderless mode and flag + SDL_SetWindowBordered(platform.window, SDL_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + + // Remove topmost modes and flag + SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + + // Restore the window's previous size and position + SDL_SetWindowSize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + SDL_SetWindowPosition(platform.window, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + + // Remove borderless windowed flag + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } } // Set window state: maximized, if resizable @@ -253,19 +313,247 @@ void RestoreWindow(void) // Set window configuration state using flags void SetWindowState(unsigned int flags) { - //SDL_HideWindow(platform.window); + CORE.Window.flags |= flags; + + if (flags & FLAG_VSYNC_HINT) + { + SDL_GL_SetSwapInterval(1); + } + if (flags & FLAG_FULLSCREEN_MODE) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + } + if (flags & FLAG_WINDOW_RESIZABLE) + { + SDL_SetWindowResizable(platform.window, SDL_TRUE); + } + if (flags & FLAG_WINDOW_UNDECORATED) + { + SDL_SetWindowBordered(platform.window, SDL_FALSE); + } + if (flags & FLAG_WINDOW_HIDDEN) + { + SDL_HideWindow(platform.window); + } + if (flags & FLAG_WINDOW_MINIMIZED) + { + SDL_MinimizeWindow(platform.window); + } + if (flags & FLAG_WINDOW_MAXIMIZED) + { + SDL_MaximizeWindow(platform.window); + } + if (flags & FLAG_WINDOW_UNFOCUSED) + { + // NOTE: To be able to implement this part it seems that we should + // do it ourselves, via `Windows.h`, `X11/Xlib.h` or even `Cocoa.h` + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_TOPMOST) + { + SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); + } + if (flags & FLAG_WINDOW_ALWAYS_RUN) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_TRANSPARENT) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_HIGHDPI) + { + // NOTE: Such a function does not seem to exist + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) + { + //SDL_SetWindowGrab(platform.window, SDL_FALSE); + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_BORDERLESS_WINDOWED_MODE) + { + // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? + SDL_SetWindowBordered(platform.window, SDL_FALSE); + } + if (flags & FLAG_MSAA_4X_HINT) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable multisampling buffers + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); // Enable multisampling + } + if (flags & FLAG_INTERLACED_HINT) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL"); + } } // Clear window configuration state flags void ClearWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); + CORE.Window.flags &= ~flags; + + if (flags & FLAG_VSYNC_HINT) + { + SDL_GL_SetSwapInterval(0); + } + if (flags & FLAG_FULLSCREEN_MODE) + { + SDL_SetWindowFullscreen(platform.window, 0); + } + if (flags & FLAG_WINDOW_RESIZABLE) + { + SDL_SetWindowResizable(platform.window, SDL_FALSE); + } + if (flags & FLAG_WINDOW_UNDECORATED) + { + SDL_SetWindowBordered(platform.window, SDL_TRUE); + } + if (flags & FLAG_WINDOW_HIDDEN) + { + SDL_ShowWindow(platform.window); + } + if (flags & FLAG_WINDOW_MINIMIZED) + { + SDL_RestoreWindow(platform.window); + } + if (flags & FLAG_WINDOW_MAXIMIZED) + { + SDL_RestoreWindow(platform.window); + } + if (flags & FLAG_WINDOW_UNFOCUSED) + { + //SDL_RaiseWindow(platform.window); + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_TOPMOST) + { + SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); + } + if (flags & FLAG_WINDOW_ALWAYS_RUN) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_TRANSPARENT) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_HIGHDPI) + { + // NOTE: There also doesn't seem to be a feature to disable high DPI once enabled + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) + { + //SDL_SetWindowGrab(platform.window, SDL_TRUE); + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_BORDERLESS_WINDOWED_MODE) + { + // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? + SDL_SetWindowBordered(platform.window, SDL_TRUE); + } + if (flags & FLAG_MSAA_4X_HINT) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); // Disable multisampling buffers + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); // Disable multisampling + } + if (flags & FLAG_INTERLACED_HINT) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL"); + } } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); + SDL_Surface* iconSurface = NULL; + + Uint32 rmask, gmask, bmask, amask; + int depth = 0; // Depth in bits + int pitch = 0; // Pixel spacing (pitch) in bytes + + switch (image.format) + { + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: + rmask = 0xFF, gmask = 0; + bmask = 0, amask = 0; + depth = 8, pitch = image.width; + break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + rmask = 0xFF, gmask = 0xFF00; + bmask = 0, amask = 0; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R5G6B5: + rmask = 0xF800, gmask = 0x07E0; + bmask = 0x001F, amask = 0; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: + rmask = 0xFF0000, gmask = 0x00FF00; + bmask = 0x0000FF, amask = 0; + depth = 24, pitch = image.width * 3; + break; + case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + rmask = 0xF800, gmask = 0x07C0; + bmask = 0x003E, amask = 0x0001; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: + rmask = 0xF000, gmask = 0x0F00; + bmask = 0x00F0, amask = 0x000F; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: + rmask = 0xFF000000, gmask = 0x00FF0000; + bmask = 0x0000FF00, amask = 0x000000FF; + depth = 32, pitch = image.width * 4; + break; + case PIXELFORMAT_UNCOMPRESSED_R32: + rmask = 0xFFFFFFFF, gmask = 0; + bmask = 0, amask = 0; + depth = 32, pitch = image.width * 4; + break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32: + rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; + bmask = 0xFFFFFFFF, amask = 0; + depth = 96, pitch = image.width * 12; + break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: + rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; + bmask = 0xFFFFFFFF, amask = 0xFFFFFFFF; + depth = 128, pitch = image.width * 16; + break; + case PIXELFORMAT_UNCOMPRESSED_R16: + rmask = 0xFFFF, gmask = 0; + bmask = 0, amask = 0; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + rmask = 0xFFFF, gmask = 0xFFFF; + bmask = 0xFFFF, amask = 0; + depth = 48, pitch = image.width * 6; + break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + rmask = 0xFFFF, gmask = 0xFFFF; + bmask = 0xFFFF, amask = 0xFFFF; + depth = 64, pitch = image.width * 8; + break; + default: + // Compressed formats are not supported + return; + } + + iconSurface = SDL_CreateRGBSurfaceFrom( + image.data, image.width, image.height, depth, pitch, + rmask, gmask, bmask, amask + ); + + if (iconSurface) + { + SDL_SetWindowIcon(platform.window, iconSurface); + SDL_FreeSurface(iconSurface); + } } // Set icon for window @@ -294,7 +582,20 @@ void SetWindowPosition(int x, int y) // Set monitor for the current window void SetWindowMonitor(int monitor) { - TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); + if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + { + TRACELOG(LOG_ERROR, "Invalid monitor index"); + return; + } + + SDL_Rect displayBounds; + if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) + { + TRACELOG(LOG_ERROR, "Failed to get display bounds"); + return; + } + + SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -360,8 +661,20 @@ int GetCurrentMonitor(void) // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); - return (Vector2){ 0, 0 }; + if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + { + TRACELOG(LOG_ERROR, "Invalid monitor index"); + return (Vector2) { 0, 0 }; + } + + SDL_Rect displayBounds; + if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) + { + TRACELOG(LOG_ERROR, "Failed to get display bounds"); + return (Vector2) { 0, 0 }; + } + + return (Vector2) { displayBounds.x, displayBounds.y }; } // Get selected monitor width (currently used by monitor) @@ -530,6 +843,7 @@ void EnableCursor(void) SDL_SetRelativeMouseMode(SDL_FALSE); SDL_ShowCursor(SDL_ENABLE); + platform.cursorRelative = false; CORE.Input.Mouse.cursorHidden = false; } @@ -538,6 +852,7 @@ void DisableCursor(void) { SDL_SetRelativeMouseMode(SDL_TRUE); + platform.cursorRelative = true; CORE.Input.Mouse.cursorHidden = true; } @@ -572,7 +887,7 @@ void OpenURL(const char *url) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - SDL_GameControllerAddMapping(mappings); + return SDL_GameControllerAddMapping(mappings); } // Set mouse position XY @@ -612,7 +927,8 @@ void PollInputEvents(void) CORE.Input.Mouse.currentWheelMove.y = 0; // Register previous mouse position - CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + if (platform.cursorRelative) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; + else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; @@ -628,7 +944,7 @@ void PollInputEvents(void) // Register previous keys states // NOTE: Android supports up to 260 keys - for (int i = 0; i < 260; i++) + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) { CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; @@ -662,16 +978,16 @@ void PollInputEvents(void) { switch (event.window.event) { - case SDL_WINDOWEVENT_LEAVE: - case SDL_WINDOWEVENT_HIDDEN: - case SDL_WINDOWEVENT_MINIMIZED: - case SDL_WINDOWEVENT_FOCUS_LOST: - case SDL_WINDOWEVENT_ENTER: - case SDL_WINDOWEVENT_SHOWN: - case SDL_WINDOWEVENT_FOCUS_GAINED: - case SDL_WINDOWEVENT_MAXIMIZED: - case SDL_WINDOWEVENT_RESTORED: - default: break; + case SDL_WINDOWEVENT_LEAVE: + case SDL_WINDOWEVENT_HIDDEN: + case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_FOCUS_LOST: + case SDL_WINDOWEVENT_ENTER: + case SDL_WINDOWEVENT_SHOWN: + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_MAXIMIZED: + case SDL_WINDOWEVENT_RESTORED: + default: break; } } break; @@ -710,8 +1026,17 @@ void PollInputEvents(void) } break; case SDL_MOUSEMOTION: { - CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; - CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; + if (platform.cursorRelative) + { + CORE.Input.Mouse.currentPosition.x = (float)event.motion.xrel; + CORE.Input.Mouse.currentPosition.y = (float)event.motion.yrel; + CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; + } + else + { + CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; + CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; + } } break; // Check gamepad events @@ -790,6 +1115,12 @@ static int InitPlatform(void) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + + if (CORE.Window.flags & FLAG_VSYNC_HINT) + { + SDL_GL_SetSwapInterval(1); + } + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); @@ -807,6 +1138,12 @@ static int InitPlatform(void) { CORE.Window.ready = true; + SDL_DisplayMode displayMode; + SDL_GetCurrentDisplayMode(GetCurrentMonitor(), &displayMode); + + CORE.Window.display.width = displayMode.w; + CORE.Window.display.height = displayMode.h; + CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; From 19ff0e5fb1bbac46921ab34f922e77461c13c11f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:05:50 +0200 Subject: [PATCH 0072/1037] REVIEWED: `rlLoadTexture()` #3440 --- src/rlgl.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 40e7b4785..e38642ec9 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3000,7 +3000,8 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipOffset = 0; // Mipmap data offset, only used for tracelog // NOTE: Added pointer math separately from function to avoid UBSAN complaining - unsigned char *dataPtr = (unsigned char *)data; + unsigned char *dataPtr = NULL; + if (data != NULL) dataPtr = (unsigned char *)data; // Load the different mipmap levels for (int i = 0; i < mipmapCount; i++) @@ -3040,7 +3041,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, mipWidth /= 2; mipHeight /= 2; mipOffset += mipSize; // Increment offset position to next mipmap - dataPtr += mipSize; // Increment data pointer to next mipmap + if (data != NULL) dataPtr += mipSize; // Increment data pointer to next mipmap // Security check for NPOT textures if (mipWidth < 1) mipWidth = 1; From a64d606cb3aa55df0f01f75b8db0ca6b9a06b562 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 19 Oct 2023 08:09:27 -0300 Subject: [PATCH 0073/1037] Fix GetMonitorPhysical* dpi (#3442) --- src/rcore_desktop_sdl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index fedebeee4..c574b8199 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -230,7 +230,7 @@ void ToggleFullscreen(void) else { SDL_SetWindowFullscreen(platform.window, 0); - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; } } @@ -725,12 +725,12 @@ int GetMonitorPhysicalWidth(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - float vdpi = 0.0f; - SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + float ddpi = 0.0f; + SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL); SDL_DisplayMode mode; SDL_GetCurrentDisplayMode(monitor, &mode); // Calculate size on inches, then convert to millimeter - if (vdpi > 0.0f) width = (mode.w/vdpi)*25.4f; + if (ddpi > 0.0f) width = (mode.w/ddpi)*25.4f; } else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); @@ -747,12 +747,12 @@ int GetMonitorPhysicalHeight(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - float vdpi = 0.0f; - SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + float ddpi = 0.0f; + SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL); SDL_DisplayMode mode; SDL_GetCurrentDisplayMode(monitor, &mode); // Calculate size on inches, then convert to millimeter - if (vdpi > 0.0f) height = (mode.h/vdpi)*25.4f; + if (ddpi > 0.0f) height = (mode.h/ddpi)*25.4f; } else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); From 982641228c6ee5732ae99a9c26895305143b21d9 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:36:10 +0200 Subject: [PATCH 0074/1037] REDESIGNED: Move platforms to separate directory #3313 --- src/{ => platforms}/rcore_android.c | 0 src/{ => platforms}/rcore_desktop.c | 0 src/{ => platforms}/rcore_desktop_sdl.c | 0 src/{ => platforms}/rcore_drm.c | 0 src/{ => platforms}/rcore_template.c | 0 src/{ => platforms}/rcore_web.c | 0 src/rcore.c | 32 ++++++++++++++++++------- 7 files changed, 23 insertions(+), 9 deletions(-) rename src/{ => platforms}/rcore_android.c (100%) rename src/{ => platforms}/rcore_desktop.c (100%) rename src/{ => platforms}/rcore_desktop_sdl.c (100%) rename src/{ => platforms}/rcore_drm.c (100%) rename src/{ => platforms}/rcore_template.c (100%) rename src/{ => platforms}/rcore_web.c (100%) diff --git a/src/rcore_android.c b/src/platforms/rcore_android.c similarity index 100% rename from src/rcore_android.c rename to src/platforms/rcore_android.c diff --git a/src/rcore_desktop.c b/src/platforms/rcore_desktop.c similarity index 100% rename from src/rcore_desktop.c rename to src/platforms/rcore_desktop.c diff --git a/src/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c similarity index 100% rename from src/rcore_desktop_sdl.c rename to src/platforms/rcore_desktop_sdl.c diff --git a/src/rcore_drm.c b/src/platforms/rcore_drm.c similarity index 100% rename from src/rcore_drm.c rename to src/platforms/rcore_drm.c diff --git a/src/rcore_template.c b/src/platforms/rcore_template.c similarity index 100% rename from src/rcore_template.c rename to src/platforms/rcore_template.c diff --git a/src/rcore_web.c b/src/platforms/rcore_web.c similarity index 100% rename from src/rcore_web.c rename to src/platforms/rcore_web.c diff --git a/src/rcore.c b/src/rcore.c index c6770a5bb..fd0335c6e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -281,8 +281,8 @@ extern void LoadFontDefault(void); // [Module: text] Loads default font on extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +extern int InitPlatform(void); // Initialize platform (graphics, inputs and more) +extern void ClosePlatform(void); // Close platform static void InitTimer(void); // Initialize timer (hi-resolution if available) static void SetupFramebuffer(int width, int height); // Setup main framebuffer @@ -309,15 +309,15 @@ const char *TextFormat(const char *text, ...); // Formatting of text with // Include platform-specific submodules #if defined(PLATFORM_DESKTOP) - #include "rcore_desktop.c" + #include "platforms/rcore_desktop.c" #elif defined(PLATFORM_DESKTOP_SDL) - #include "rcore_desktop_sdl.c" + #include "platforms/rcore_desktop_sdl.c" #elif defined(PLATFORM_WEB) - #include "rcore_web.c" + #include "platforms/rcore_web.c" #elif defined(PLATFORM_DRM) - #include "rcore_drm.c" + #include "platforms/rcore_drm.c" #elif defined(PLATFORM_ANDROID) - #include "rcore_android.c" + #include "platforms/rcore_android.c" #else // TODO: Include your custom platform backend! // i.e software rendering backend or console backend! @@ -328,8 +328,6 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//void InitWindow(int width, int height, const char *title) -//void CloseWindow(void) //bool WindowShouldClose(void) //void ToggleFullscreen(void) //void ToggleBorderlessWindowed(void) @@ -378,6 +376,22 @@ void InitWindow(int width, int height, const char *title) { TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); +#if defined(PLATFORM_DESKTOP) + TRACELOG(LOG_INFO, "Platform backend: DESKTOP (GLFW)"); +#elif defined(PLATFORM_DESKTOP_SDL) + TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)"); +#elif defined(PLATFORM_WEB) + TRACELOG(LOG_INFO, "Platform backend: WEB (HTML5)"); +#elif defined(PLATFORM_DRM) + TRACELOG(LOG_INFO, "Platform backend: NATIVE DRM"); +#elif defined(PLATFORM_ANDROID) + TRACELOG(LOG_INFO, "Platform backend: ANDROID"); +#else + // TODO: Include your custom platform backend! + // i.e software rendering backend or console backend! + TRACELOG(LOG_INFO, "Platform backend: CUSTOM"); +#endif + TRACELOG(LOG_INFO, "Supported raylib modules:"); TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); From 65dd0afb60770f88d863283aed7098a474535183 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:41:09 +0200 Subject: [PATCH 0075/1037] Update Makefile --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index e6c5106cc..ea8bdd5e5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -633,7 +633,7 @@ endif # Compile all modules with their prerequisites # Prerequisites of core module -rcore.o : rcore_android.c rcore_desktop.c rcore_drm.c rcore_template.c rcore_web.c +rcore.o : platforms/rcore_android.c platforms/rcore_desktop.c platforms/rcore_drm.c platforms/rcore_template.c platforms/rcore_web.c # Compile core module rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h From b674e344a878613881d5e8d0e9f86176e4c0a56f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:46:02 +0200 Subject: [PATCH 0076/1037] REVIEWED: Issue with symbols exposure --- src/platforms/rcore_android.c | 4 ++-- src/platforms/rcore_desktop.c | 4 ++-- src/platforms/rcore_desktop_sdl.c | 5 +++-- src/platforms/rcore_drm.c | 4 ++-- src/platforms/rcore_template.c | 4 ++-- src/platforms/rcore_web.c | 4 ++-- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index bf55ece27..ddf0c7a45 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -80,8 +80,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 29969d4ca..4dec2c77b 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -111,8 +111,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform // Error callback event static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index c574b8199..b64724334 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -199,8 +199,9 @@ static const int CursorsLUT[] = { //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform + static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help convert SDL scancodes to raylib key //---------------------------------------------------------------------------------- diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 3302ec362..4df40e5b0 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -138,8 +138,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform static void InitKeyboard(void); // Initialize raw keyboard system static void RestoreKeyboard(void); // Restore keyboard system diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index f94cca8c1..87aca16da 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -71,8 +71,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static bool InitGraphicsDevice(void); // Initialize graphics device +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +bool InitGraphicsDevice(void); // Initialize graphics device //---------------------------------------------------------------------------------- // Module Functions Declaration diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 465bb8957..bfc5bd790 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -85,8 +85,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform // Error callback event static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error From 081fffd46e56ae1e6f80b12bf8fe5728828def22 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:57:31 +0200 Subject: [PATCH 0077/1037] REVIEWED: Issue with functions definitions --- src/platforms/rcore_android.c | 5 +++-- src/platforms/rcore_desktop.c | 4 ++-- src/platforms/rcore_desktop_sdl.c | 5 +++-- src/platforms/rcore_drm.c | 4 ++-- src/platforms/rcore_template.c | 4 ++-- src/platforms/rcore_web.c | 5 +++-- src/rcore.c | 3 ++- 7 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index ddf0c7a45..1272eecc3 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -516,7 +516,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { CORE.Window.currentFbo.width = CORE.Window.screen.width; CORE.Window.currentFbo.height = CORE.Window.screen.height; @@ -589,7 +589,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { // Close surface, context and display if (platform.device != EGL_NO_DISPLAY) @@ -613,6 +613,7 @@ static void ClosePlatform(void) } } + // Initialize display device and framebuffer // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 4dec2c77b..ea1b15fad 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1214,7 +1214,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { glfwSetErrorCallback(ErrorCallback); /* @@ -1531,7 +1531,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { glfwDestroyWindow(platform.handle); glfwTerminate(); diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index b64724334..c57a94fe9 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1070,7 +1070,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { // Initialize SDL internal global state int result = SDL_Init(SDL_INIT_EVERYTHING); @@ -1180,7 +1180,7 @@ static int InitPlatform(void) return 0; } -static void ClosePlatform(void) +void ClosePlatform(void) { SDL_FreeCursor(platform.cursor); // Free cursor SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context @@ -1188,6 +1188,7 @@ static void ClosePlatform(void) SDL_Quit(); // Deinitialize SDL internal global state } + static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) { if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 4df40e5b0..2ecfc2a3a 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -565,7 +565,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { platform.fd = -1; platform.connector = NULL; @@ -916,7 +916,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { if (platform.prevFB) { diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 87aca16da..dc71a66ca 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -429,7 +429,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -556,7 +556,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { // TODO: De-initialize graphics, inputs and more } diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index bfc5bd790..e918e7d34 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -669,7 +669,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { glfwSetErrorCallback(ErrorCallback); @@ -932,12 +932,13 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { glfwDestroyWindow(platform.handle); glfwTerminate(); } + // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/rcore.c b/src/rcore.c index fd0335c6e..e3cd08188 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2523,7 +2523,8 @@ int GetTouchPointCount(void) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//static bool InitPlatform(void) +//int InitPlatform(void) +//void ClosePlatform(void) // Initialize hi-resolution timer void InitTimer(void) From c66eb491992bebf4462d61cfb9c232f7cfefba28 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 20 Oct 2023 12:38:14 -0300 Subject: [PATCH 0078/1037] [core] Complement implementations for `SDL` (#3444) * Complement SetWindowMonitor SDL implementation * Complement SetWindowMonitor SDL implementation 2 * Complement SetWindowMonitor SDL implementation 3 * Complement GetMonitorPosition SDL implementation * Small tweaks to various SDL implementation * Small tweaks to various SDL implementation 2 --- src/platforms/rcore_desktop_sdl.c | 104 ++++++++++++++++++------------ 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index c57a94fe9..1b52a960d 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -583,20 +583,51 @@ void SetWindowPosition(int x, int y) // Set monitor for the current window void SetWindowMonitor(int monitor) { - if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - TRACELOG(LOG_ERROR, "Invalid monitor index"); - return; - } + // NOTE: + // 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3, + // see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba + // 2. A workround for SDL2 is leaving fullscreen, moving the window, then entering full screen again. + const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) ? true : false; - SDL_Rect displayBounds; - if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) - { - TRACELOG(LOG_ERROR, "Failed to get display bounds"); - return; - } + const int screenWidth = CORE.Window.screen.width; + const int screenHeight = CORE.Window.screen.height; + SDL_Rect usableBounds; + if (SDL_GetDisplayUsableBounds(monitor, &usableBounds) == 0) + { + if (wasFullscreen == 1) ToggleFullscreen(); // Leave fullscreen. - SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); + // If the screen size is larger than the monitor usable area, anchor it on the top left corner, otherwise, center it + if ((screenWidth >= usableBounds.w) || (screenHeight >= usableBounds.h)) + { + // NOTE: + // 1. There's a known issue where if the window larger than the target display bounds, + // when moving the windows to that display, the window could be clipped back + // ending up positioned partly outside the target display. + // 2. The workaround for that is, previously to moving the window, + // setting the window size to the target display size, so they match. + // 3. It was't done here because we can't assume changing the window size automatically + // is acceptable behavior by the user. + SDL_SetWindowPosition(platform.window, usableBounds.x, usableBounds.y); + CORE.Window.position.x = usableBounds.x; + CORE.Window.position.y = usableBounds.y; + } + else + { + const int x = usableBounds.x + (usableBounds.w/2) - (screenWidth/2); + const int y = usableBounds.y + (usableBounds.h/2) - (screenHeight/2); + SDL_SetWindowPosition(platform.window, x, y); + CORE.Window.position.x = x; + CORE.Window.position.y = y; + } + + if (wasFullscreen == 1) ToggleFullscreen(); // Re-enter fullscreen + } + else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds"); + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -656,26 +687,28 @@ int GetMonitorCount(void) // Get number of monitors int GetCurrentMonitor(void) { - return SDL_GetWindowDisplayIndex(platform.window); + int currentMonitor = 0; + + currentMonitor = SDL_GetWindowDisplayIndex(platform.window); + + return currentMonitor; } // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - TRACELOG(LOG_ERROR, "Invalid monitor index"); - return (Vector2) { 0, 0 }; + SDL_Rect displayBounds; + if (SDL_GetDisplayUsableBounds(monitor, &displayBounds) == 0) + { + return (Vector2){ (float)displayBounds.x, (float)displayBounds.y }; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds"); } - - SDL_Rect displayBounds; - if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) - { - TRACELOG(LOG_ERROR, "Failed to get display bounds"); - return (Vector2) { 0, 0 }; - } - - return (Vector2) { displayBounds.x, displayBounds.y }; + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + return (Vector2){ 0.0f, 0.0f }; } // Get selected monitor width (currently used by monitor) @@ -683,9 +716,7 @@ int GetMonitorWidth(int monitor) { int width = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { SDL_DisplayMode mode; @@ -702,9 +733,7 @@ int GetMonitorHeight(int monitor) { int height = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { SDL_DisplayMode mode; @@ -721,9 +750,7 @@ int GetMonitorPhysicalWidth(int monitor) { int width = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { float ddpi = 0.0f; @@ -743,9 +770,7 @@ int GetMonitorPhysicalHeight(int monitor) { int height = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { float ddpi = 0.0f; @@ -765,9 +790,7 @@ int GetMonitorRefreshRate(int monitor) { int refresh = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { SDL_DisplayMode mode; @@ -782,8 +805,7 @@ int GetMonitorRefreshRate(int monitor) // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) return SDL_GetDisplayName(monitor); else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); From e5993c4a4b27cb30617f1d07b0d9583e8f556902 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 21 Oct 2023 07:11:54 -0300 Subject: [PATCH 0079/1037] [core] Complement implementations for `SDL` (2) (#3447) * Add note and todo to GetWindowScaleDPI * Complement ToggleFullscreen and change ToggleBorderlessWindowed * Complement SetWindowState and ClearWindowState --- src/platforms/rcore_desktop_sdl.c | 113 ++++++++++++++---------------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 1b52a960d..b261b0428 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -223,72 +223,45 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - if (!IsWindowState(FLAG_FULLSCREEN_MODE)) + const int monitor = SDL_GetWindowDisplayIndex(platform.window); + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - } - else - { - SDL_SetWindowFullscreen(platform.window, 0); - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) + { + SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + CORE.Window.fullscreen = false; + } + else + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + CORE.Window.fullscreen = true; + } } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it - bool wasOnFullscreen = false; - if (CORE.Window.fullscreen) + const int monitor = SDL_GetWindowDisplayIndex(platform.window); + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - CORE.Window.previousPosition = CORE.Window.position; - ToggleFullscreen(); - wasOnFullscreen = true; - } - - if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) - { - // Store the window's current position and size - SDL_GetWindowPosition(platform.window, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); - CORE.Window.previousScreen = CORE.Window.screen; - - // Set screen position and size inside valid bounds - SDL_Rect displayBounds; - if (SDL_GetDisplayBounds(GetCurrentMonitor(), &displayBounds) == 0) + if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) { - SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); - SDL_SetWindowSize(platform.window, displayBounds.w, displayBounds.h); + SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP); + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; } - - // Set borderless mode and flag - SDL_SetWindowBordered(platform.window, SDL_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - - // Set topmost modes and flag - SDL_SetWindowAlwaysOnTop(platform.window, SDL_TRUE); - CORE.Window.flags |= FLAG_WINDOW_TOPMOST; - - // Set borderless windowed flag - CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; - } - else - { - // Remove borderless mode and flag - SDL_SetWindowBordered(platform.window, SDL_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - - // Remove topmost modes and flag - SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - - // Restore the window's previous size and position - SDL_SetWindowSize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); - SDL_SetWindowPosition(platform.window, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); - - // Remove borderless windowed flag - CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } // Set window state: maximized, if resizable @@ -322,7 +295,14 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_FULLSCREEN_MODE) { - SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + const int monitor = SDL_GetWindowDisplayIndex(platform.window); + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + CORE.Window.fullscreen = true; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } if (flags & FLAG_WINDOW_RESIZABLE) { @@ -374,8 +354,13 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { - // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? - SDL_SetWindowBordered(platform.window, SDL_FALSE); + const int monitor = SDL_GetWindowDisplayIndex(platform.window); + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP); + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } if (flags & FLAG_MSAA_4X_HINT) { @@ -400,6 +385,7 @@ void ClearWindowState(unsigned int flags) if (flags & FLAG_FULLSCREEN_MODE) { SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.fullscreen = false; } if (flags & FLAG_WINDOW_RESIZABLE) { @@ -450,8 +436,7 @@ void ClearWindowState(unsigned int flags) } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { - // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? - SDL_SetWindowBordered(platform.window, SDL_TRUE); + SDL_SetWindowFullscreen(platform.window, 0); } if (flags & FLAG_MSAA_4X_HINT) { @@ -827,8 +812,14 @@ Vector2 GetWindowPosition(void) // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { + Vector2 scale = { 1.0f, 1.0f }; + + // NOTE: SDL_GetWindowDisplayScale was only added on SDL3 + // see https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale + // TODO: Implement the window scale factor calculation manually. TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); - return (Vector2){ 1.0f, 1.0f }; + + return scale; } // Set clipboard text content From 8cda4273ece19fe349bad73cb7e118352ea1cc42 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 22 Oct 2023 04:45:04 -0300 Subject: [PATCH 0080/1037] [core] Complement implementations for `SDL` (3) (#3450) * Fix SetWindowMinSize and SetWindowMaxSize * Fix window resizes to update the viewport * Fix window resizes to update the viewport 2 --- src/platforms/rcore_desktop_sdl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index b261b0428..d2f550ace 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -618,6 +618,8 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { + SDL_SetWindowMinimumSize(platform.window, width, height); + CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; } @@ -625,6 +627,8 @@ void SetWindowMinSize(int width, int height) // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMaxSize(int width, int height) { + SDL_SetWindowMaximumSize(platform.window, width, height); + CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; } @@ -979,6 +983,8 @@ void PollInputEvents(void) } */ + CORE.Window.resizedLastFrame = false; + SDL_Event event = { 0 }; while (SDL_PollEvent(&event) != 0) { @@ -992,6 +998,18 @@ void PollInputEvents(void) { switch (event.window.event) { + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + { + const int width = event.window.data1; + const int height = event.window.data2; + SetupViewport(width, height); + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + } break; case SDL_WINDOWEVENT_LEAVE: case SDL_WINDOWEVENT_HIDDEN: case SDL_WINDOWEVENT_MINIMIZED: From bcfa7c6718202d7697c1af4019d0dd4f30eb12e5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:08:39 +0200 Subject: [PATCH 0081/1037] Update rcore_desktop.c --- src/platforms/rcore_desktop.c | 43 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index ea1b15fad..9e653dd0e 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1349,7 +1349,7 @@ int InitPlatform(void) // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn. // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience. // REF: https://github.com/raysan5/raylib/issues/1554 - if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); + glfwSetJoystickCallback(NULL); // Find monitor resolution GLFWmonitor *monitor = glfwGetPrimaryMonitor(); @@ -1404,6 +1404,7 @@ int InitPlatform(void) } } } + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, @@ -1447,27 +1448,8 @@ int InitPlatform(void) TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); return -1; } - - // Set window callback events - glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! - glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); - glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); - glfwSetDropCallback(platform.handle, WindowDropCallback); - - // Set input callback events - glfwSetKeyCallback(platform.handle, KeyCallback); - glfwSetCharCallback(platform.handle, CharCallback); - glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); - glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(platform.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); - glfwSetJoystickCallback(JoystickCallback); - + glfwMakeContextCurrent(platform.handle); - - glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) - glfwSwapInterval(0); // No V-Sync by default // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) @@ -1520,6 +1502,25 @@ int InitPlatform(void) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + + + // Set window callback events + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); + glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); + glfwSetDropCallback(platform.handle, WindowDropCallback); + + // Set input callback events + glfwSetKeyCallback(platform.handle, KeyCallback); + glfwSetCharCallback(platform.handle, CharCallback); + glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); + glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(platform.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); + glfwSetJoystickCallback(JoystickCallback); + + glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) // Initialize hi-res timer InitTimer(); From 1aad6a2fc0eb35c9aff845c84b2a4d798be67b27 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:09:03 +0200 Subject: [PATCH 0082/1037] REVIEWED: New platform backend template comments --- src/platforms/rcore_template.c | 75 +++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index dc71a66ca..08210289e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -431,6 +431,12 @@ void PollInputEvents(void) // Initialize platform: graphics, inputs and more int InitPlatform(void) { + // TODO: Initialize graphic device: display/window + // It usually requires setting up the platform display system configuration + // and connexion with the GPU through some system graphic API + // raylib uses OpenGL so, platform should create that kind of connection + // Below example illustrates that process using EGL library + //---------------------------------------------------------------------------- CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -496,61 +502,66 @@ int InitPlatform(void) } // Create an EGL window surface - //--------------------------------------------------------------------------------- EGLint displayFormat = 0; // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - // 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); - - ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); - //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size + // Android specific call + ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL); // There must be at least one frame displayed before the buffers are swapped - //eglSwapInterval(platform.device, 1); + eglSwapInterval(platform.device, 1); - if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) + EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); + + // Enabling current display surface and context failed + if (result == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); return -1; } - else - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - CORE.Window.currentFbo.width = CORE.Window.render.width; - CORE.Window.currentFbo.height = CORE.Window.render.height; + else CORE.Window.ready = true; + //---------------------------------------------------------------------------- + + // If everything work as expected, we can continue + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - } + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - // Load OpenGL extensions + // TODO: Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions + //---------------------------------------------------------------------------- rlLoadExtensions(eglGetProcAddress); + //---------------------------------------------------------------------------- + + // TODO: Initialize input system + // It could imply keyboard, mouse, gamepad, touch... + // Depending on the platform libraries/SDK it could use a callbacks mechanims + // For system events and inputs evens polling on a per-frame basis, use PollInputEvents() + //---------------------------------------------------------------------------- + // ... + //---------------------------------------------------------------------------- - CORE.Window.ready = true; - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } - - // Initialize hi-res timer + // TODO: Initialize hi-res timer + //---------------------------------------------------------------------------- InitTimer(); + //---------------------------------------------------------------------------- - // Initialize base path for storage + // TODO: Initialize base path for storage + //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- return 0; } From c4fb6c8517d8480afffafdff81155dd35a600f73 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:27:22 +0200 Subject: [PATCH 0083/1037] REVIEWED: sinfl, fix #3349 --- src/external/sinfl.h | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 8979fcd7f..03f0b1536 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -5,6 +5,8 @@ which implements the Deflate (RFC 1951) compressed data format specification sta It is mainly tuned to get as much speed and compression ratio from as little code as needed to keep the implementation as concise as possible. +@raysan5: this file has been reviewed as per https://github.com/raysan5/raylib/issues/3349 + ## Features - Portable single header and source file duo written in ANSI C (ISO C90) - Dual license with either MIT or public domain @@ -122,6 +124,7 @@ extern "C" { struct sinfl { const unsigned char *bitptr; + const unsigned char *bitend; // @raysan5, added unsigned long long bitbuf; int bitcnt; @@ -177,17 +180,23 @@ sinfl_bsr(unsigned n) { return 31 - __builtin_clz(n); #endif } -static unsigned long long -sinfl_read64(const void *p) { - unsigned long long n; - memcpy(&n, p, 8); - return n; -} +// @raysan5, commented +//static unsigned long long +//sinfl_read64(const void *p) { +// unsigned long long n; +// memcpy(&n, p, 8); +// return n; +//} static void sinfl_copy64(unsigned char **dst, unsigned char **src) { - unsigned long long n; - memcpy(&n, *src, 8); - memcpy(*dst, &n, 8); + // @raysan5, reviewed + //---------------------------- + //unsigned long long n; + //memcpy(&n, *src, 8); + //memcpy(*dst, &n, 8); + memcpy(*dst, *src, 8); + //---------------------------- + *dst += 8, *src += 8; } static unsigned char* @@ -210,7 +219,14 @@ sinfl_copy128(unsigned char **dst, unsigned char **src) { #endif static void sinfl_refill(struct sinfl *s) { - s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; + // @raysan5, reviewed + //--------------------------------------------------- + //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; + unsigned long long n = 0; + memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); + s->bitbuf |= n << s->bitcnt; + //--------------------------------------------------- + s->bitptr += (63 - s->bitcnt) >> 3; s->bitcnt |= 56; /* bitcount in range [56,63] */ } @@ -384,6 +400,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) int last = 0; s.bitptr = in; + s.bitend = e; // @raysan5, added + while (1) { switch (state) { case hdr: { From da9bc564d2534ac447b0a22761e38ed2fd3e717b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:31:35 +0200 Subject: [PATCH 0084/1037] Update sinfl.h --- src/external/sinfl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 03f0b1536..fd9668dcc 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -223,7 +223,7 @@ sinfl_refill(struct sinfl *s) { //--------------------------------------------------- //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; unsigned long long n = 0; - memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); + memcpy(&n, s->bitptr, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); s->bitbuf |= n << s->bitcnt; //--------------------------------------------------- From ea325c54e89743432eef7b22e724515b53501767 Mon Sep 17 00:00:00 2001 From: Keith Stellyes Date: Sun, 22 Oct 2023 05:58:35 -0700 Subject: [PATCH 0085/1037] fix examples Makefile to use Makefile.Web when building for web (#3449) Co-authored-by: Keith Stellyes --- examples/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 74b3b874d..136a0ab47 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -156,7 +156,7 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) MAKE = mingw32-make endif ifeq ($(PLATFORM),PLATFORM_WEB) - MAKE = mingw32-make + MAKE = emmake make endif # Define compiler flags: CFLAGS @@ -533,6 +533,8 @@ others: $(OTHERS) %: %.c ifeq ($(PLATFORM),PLATFORM_ANDROID) $(MAKE) -f Makefile.Android PROJECT_NAME=$@ PROJECT_SOURCE_FILES=$< +else ifeq ($(PLATFORM),PLATFORM_WEB) + $(MAKE) -f Makefile.Web $@ else $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) endif From 2b90b5600c28a6478eff7c2114eb8f7bdb6600dd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:15:56 +0200 Subject: [PATCH 0086/1037] Revert "Update sinfl.h" This reverts commit da9bc564d2534ac447b0a22761e38ed2fd3e717b. --- src/external/sinfl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index fd9668dcc..03f0b1536 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -223,7 +223,7 @@ sinfl_refill(struct sinfl *s) { //--------------------------------------------------- //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; unsigned long long n = 0; - memcpy(&n, s->bitptr, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); + memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); s->bitbuf |= n << s->bitcnt; //--------------------------------------------------- From 0e029f719b4a4afb8fee4438cf011e26fa8bdc15 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:17:35 +0200 Subject: [PATCH 0087/1037] Revert "REVIEWED: sinfl, fix #3349" This reverts commit c4fb6c8517d8480afffafdff81155dd35a600f73. --- src/external/sinfl.h | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 03f0b1536..8979fcd7f 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -5,8 +5,6 @@ which implements the Deflate (RFC 1951) compressed data format specification sta It is mainly tuned to get as much speed and compression ratio from as little code as needed to keep the implementation as concise as possible. -@raysan5: this file has been reviewed as per https://github.com/raysan5/raylib/issues/3349 - ## Features - Portable single header and source file duo written in ANSI C (ISO C90) - Dual license with either MIT or public domain @@ -124,7 +122,6 @@ extern "C" { struct sinfl { const unsigned char *bitptr; - const unsigned char *bitend; // @raysan5, added unsigned long long bitbuf; int bitcnt; @@ -180,23 +177,17 @@ sinfl_bsr(unsigned n) { return 31 - __builtin_clz(n); #endif } -// @raysan5, commented -//static unsigned long long -//sinfl_read64(const void *p) { -// unsigned long long n; -// memcpy(&n, p, 8); -// return n; -//} +static unsigned long long +sinfl_read64(const void *p) { + unsigned long long n; + memcpy(&n, p, 8); + return n; +} static void sinfl_copy64(unsigned char **dst, unsigned char **src) { - // @raysan5, reviewed - //---------------------------- - //unsigned long long n; - //memcpy(&n, *src, 8); - //memcpy(*dst, &n, 8); - memcpy(*dst, *src, 8); - //---------------------------- - + unsigned long long n; + memcpy(&n, *src, 8); + memcpy(*dst, &n, 8); *dst += 8, *src += 8; } static unsigned char* @@ -219,14 +210,7 @@ sinfl_copy128(unsigned char **dst, unsigned char **src) { #endif static void sinfl_refill(struct sinfl *s) { - // @raysan5, reviewed - //--------------------------------------------------- - //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; - unsigned long long n = 0; - memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); - s->bitbuf |= n << s->bitcnt; - //--------------------------------------------------- - + s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; s->bitptr += (63 - s->bitcnt) >> 3; s->bitcnt |= 56; /* bitcount in range [56,63] */ } @@ -400,8 +384,6 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) int last = 0; s.bitptr = in; - s.bitend = e; // @raysan5, added - while (1) { switch (state) { case hdr: { From cdb394fac619c5573b3c4328a5e1a9652d4186ff Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:21:25 +0200 Subject: [PATCH 0088/1037] Update CHANGELOG for **raylib 5.0** -WIP- --- CHANGELOG | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 43ab3ab44..cad709bef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,203 @@ changelog Current Release: raylib 4.5.0 (18 March 2023) +------------------------------------------------------------------------- +Release: raylib 5.0 (xx November 2023) +------------------------------------------------------------------------- +KEY CHANGES: + - REDESIGNED: rcore module split per-platform, by @ubkp, @michaelfiber, @Bigfoot71, @raysan5 + - REMOVED: Deprecated platform: PLATFORM_RPI, use PLATFORM_DRM instead + - ADDED: New platform backend supported: SDL + - ADDED: New platform backend supported: Nintendo Switch (closed source) + - REDESIGNED: Automation Events System (exposed API) + +Detailed changes: +[rcore] ADDED: RAYLIB_VERSION_* values to raylib.h (#2856) by @RobLoach +[rcore] ADDED: IsKeyPressedRepeat() on PLATFORM_DESKTOP (#3245) by @actondev +[rcore] ADDED: SetWindowTitle() for PLATFORM_WEB (#3222) by @VitusVeit +[rcore] ADDED: FLAG_WINDOW_RESIZABLE for web (#3305) by @Peter0x44 +[rcore] ADDED: SetWindowMaxSize() for desktop and web (#3309) by @ubkp +[rcore] REMOVED: PLATFORM_RPI (#3232) by @michaelfiber +[rcore] REVIEWED: GetFileLength(), added comment #3262 by @raysan5 +[rcore] REVIEWED: Default shaders precission issue on PLATFORM_WEB (#3261) by @branc116 +[rcore] REVIEWED: IsKey*() key validation checks (#3256) by @n77y +[rcore] REVIEWED: SetClipboardText() for PLATFORM_WEB (#3257) by @ubkp +[rcore] REVIEWED: Check if Ctrl modifier is among the currently set modifiers (#3230) by @mohad12211 +[rcore] REVIEWED: Android app black screen when reopening by @Bigfoot71 +[rcore] REVIEWED: Warnings when casting int to floats (#3218) by @JeffM2501 +[rcore] REVIEWED: GetCurrentMonitor() detection inconsistency issue (#3215) by @ubkp +[rcore] REVIEWED: SetWindowMonitor() to no longer force fullscreen (#3209) by @ubkp +[rcore] REVIEWED: Fix mouse wheel not working in PLATFORM_RPI or PLATFORM_DRM (#3193) by @ubkp +[rcore] REVIEWED: GetMonitorName() description (#3184) (#3189) by @danilwhale +[rcore] REVIEWED: Window flags order (#3114) by @lesleyrs +[rcore] REVIEWED: Full movement for right analog stick (#3095) by @PixelPhobicGames +[rcore] REVIEWED: Fix Android app freeze after calling CloseWindow() (#3067) by @Bigfoot71 +[rcore] REVIEWED: Lazy loading of default font used on image drawing (no InitWindow) by @raysan5 +[rcore] REVIEWED: Minor tweaks to raylib events automation system @raysan5 +[rcore] REVIEWED: GetCurrentMonitor() bugfix (#3058) by @hamyyy +[rcore] REVIEWED: Update CORE.Input.Touch.pointCount #3024 by @raysan5 +[rcore] REVIEWED: Mouse offset and scaling must be considered also on web! +[rcore] REVIEWED: CompressData(), possible stack overflow +[rcore] REVIEWED: GetWorldToScreenEx() (#3351) by @Brian-ED +[rlgl] ADDED: Experimental support for OpenGL ES 3.0 by @raysan5 +[rlgl] ADDED: Support 16-Bit HDR textures (#3220) by @Not-Nik +[rlgl] REVIEWED: Improved support for ES3/WebGL2 (#3107) by @chemaguerra +[rlgl] REVIEWED: OpenGL 2.1 half floats support as part of an extension by @Not-Nik +[rlgl] REVIEWED: Avoid shader attribute not found log by @raysan5 +[rlgl] REVIEWED: Avoid tracelog about not found uniforms #3003 by @raysan5 +[rlgl] REVIEWED: rLoadTexture() UBSAN complaints #1891 (#3321) by @Codom +[rlgl] REVIEWED: glInternalFormat as unsigned int +[rshapes] ADDED: Spline drawing functions by @raysan5 +[rshapes] REVIEWED: DrawLineCatmullRom() by @raysan5 +[rshapes] REVIEWED: Minor fix in DrawLineBezier* (#3006) by @eternalStudent +[rshapes] REVIEWED: GetCollisionRec(), more performant (#3052) by @manuel5975p +[rshapes] REVIEWED: Fix off-by-one error in CheckCollisionPointRec() (#3022) by @dbechrd +[rtextures] ADDED: Basic SVG loading support (#2738) by @bXi +[rtextures] ADDED: Support 16-Bit HDR textures (#3220) by @Not-Nik +[rtextures] ADDED: ExportImageToMemory() by @raysan5 +[rtextures] ADDED: ImageRotate() (#3078) by @danemadsen +[rtextures] ADDED: GenImageGradientSquare() (#3077) by @danemadsen +[rtextures] ADDED: GenImageLinearGradient() by @danemadsen +[rtextures] REMOVED: GenImageGradientH() and GenImageGradientV() by @danemadsen +[rtextures] REVIEWED: LoadImageSvg() by @raysan5 +[rtextures] REVIEWED: Uninitialized thread-locals in stbi #3282 (#3283) by @jbarthelmes +[rtextures] REVIEWED: ImageDrawRectangleRec(), validate drawing inside bounds by @JeffM2501 +[rtextures] REVIEWED: LoadTextureCubemap() for manual layouts (#3204) by @Not-Nik +[rtextures] REVIEWED: Optimization of ImageDrawRectangleRec() (#3185) by @smalltimewizard +[rtextures] REVIEWED: ImageRotate() formatting by @raysan5 +[rtextures] REVIEWED: GenImagePerlinNoise(), clamp values #3071 by @raysan5 +[rtextures] REVIEWED: Packing logic error in GenImageFontAtlas() (#2979) by @hanaxar +[rtextures] REVIEWED: Calculate exact image size in GenImageFontAtlas() (#2963) by @hanaxar +[rtextures] REVIEWED: ImageDrawRectangleRec() #3027 by @raysan5 +[rtextures] REVIEWED: ImageDraw() source clipping when drawing beyond top left (#3306) by @RobLoach +[rtextures] REVIEWED: UnloadRenderTexture(), additional checks +[rtext] ADDED: Font altas white rectangle and flag SUPPORT_FONT_ATLAS_WHITE_REC by @raysan5 +[rtext] ADDED: SetTextLineSpacing() to define line breaks text drawing spacing by @raysan5 +[rtext] RENAMED: LoadFont*() parameter names for consistency and coherence by @raysan5 +[rtext] REVIEWED: GetCodepointCount(), ignore unused return value of GetCodepointNext by @ashn-dot-dev +[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured. (#3399) by @Murlocohol +[rtext] REVIEWED: GetGlyphIndex() #3000 by @raysan5 +[rtext] REVIEWED: GetCodepointNext() to return default value on invalid inp… by @chocolate42 +[rtext] REVIEWED: TextToPascal() issue when first char is uppercase +[rmodels] ADDED: ModelAnimation.name field, initially with GLTF animation names loaded (… by @alfredbaudisch +[rmodels] REVIEWED: Support .vox model files version 200 (#3097) by @Bigfoot71 +[rmodels] REVIEWED: Materials loading #3126 @raysan5 +[rmodels] REVIEWED: DrawBillboardPro() to allow source of negative size (#3197) by @bohonghuang +[rmodels] REVIEWED: glTF loading segfault in animNormals memcpy by @charles-l +[rmodels] REVIEWED: LoadModelAnimationsGLTF(), free fileData after use (#3065) by @crynux +[rmodels] REVIEWED: GenMeshCubicmap(), correction of values (#3032) by @Bigfoot71 +[rmodels] REVIEWED: DrawMesh() to avoid UBSAN complaining (#1891) +[raudio] ADDED: LoadSoundAlias() by @JeffM2501 +[raudio] ADDED: Missing structure on standalone mode #3160 by @raysan5 +[raudio] REVIEWED: Comments about sample format to AttachAudioStreamProcessor() (#3188) by @AlbertoGP +[raudio] REVIEWED: Documented buffer format for audio processors (#3186) by @AlbertoGP +[raudio] REVIEWED: ExportWaveAsCode() file saving by @RadsammyT +[raudio] REVIEWED: Fix warning on discarded const qualifier (#2967) by @RobLoach +[raudio] REVIEWED: Move mutex initialization before ma_device_start() (#3325) by @Bigfoot71 +[rcamera] REVIEWED: File-macros for consistency #3161 by @raysan5 +[rcamera] REVIEWED: Support analog stick camera controls (#3066) by @PixelPhobicGames +[rcamera] REVIEWED: CameraMoveToTarget(), ensure distance is greater than 0 (#3031) by @kolunmi +[rcamera] REVIEWED: Exposing rcamera functions to the dll (#3355) by @JeffM2501 +[raymath] ADDED: Vector3Projection() and Vector3Rejection() (#3263) by @Dial0 +[raymath] ADDED: EPSILON macro to each function requiring it (#3330) by @Brian-ED +[raymath] REVIEWED: Usage of 'sinf()' and 'cosf()' to be correct (#3181) by @RokasPuzonas +[raymath] REVIEWED: Slightly optimized Vector3Normalize() (#2982) by @RicoP +[raymath] REVIEWED: Comment to clarify raymath semantics by @raysan5 +[raymath] REVIEWED: Comment about Matrix conventions by @raysan5 +[raymath] REVIEWED: Vector2Angle() and Vector2LineAngle() (#3396) by @Murlocohol +[rgestures] REVIEWED: Optimize and simplify the gesture system (#3190) by @ubkp +[rgestures] REVIEWED: GESTURE_DRAG and GESTURE_SWIPE_* issues (mostly) for web (#3183) by @ubkp +[rgestures] REVIEWED: Touch pointCount for web (#3163) by @ubkp +[rgestures] REVIEWED: IsGestureDetected() parameter type +[utils] ADDED: Security checks to file reading (memory allocations) by @raysan5 +[utils] REVIEWED: LoadFileData() potential issues with dataSize +[examples] ADDED: shaders_lightmap (#3043) by @nullstare +[examples] ADDED: core_2d_camera_split_screen (#3298) by @gabrielssanches +[examples] ADDED: LoadSoundAlias() usage example (#3223) by @JeffM2501 +[examples] ADDED: textures_tiling (#3353) by @luis605 +[examples] RENAMED: 2d_camera examples for consistency +[examples] REVIEWED: Text examples SetTextLineSpacing() to multiline examples by @raysan5 +[examples] REVIEWED: examples/shapes/shapes_collision_area.c help instructions (#3279) by @asdqwe +[examples] REVIEWED: examples/shaders/shaders_texture_outline.c help instructions (#3278) by @asdqwe +[examples] REVIEWED: examples/others/easings_testbed.c help instructions and small twe… by @asdqwe +[examples] REVIEWED: example/audio/audio_module_player.c help instructions and small b… by @asdqwe +[examples] REVIEWED: example/models/models_loading_m3d.c controls (#3269) by @asdqwe +[examples] REVIEWED: example/models/models_loading_gltf.c controls (#3268) by @asdqwe +[examples] REVIEWED: text_unicode.c example crashing (#3250) by @ubkp +[examples] REVIEWED: rlgl_standalone.c compilation issue (#3242) by @ubkp +[examples] REVIEWED: core_input_gestures for Web (#3172) by @ubkp +[examples] REVIEWED: core_input_gamepad (#3110) by @iacore +[examples] REVIEWED: examples using raygui to raygui 4.0 by @raysan5 +[build] ADDED: CMake option for SUPPORT_CUSTOM_FRAME_CONTROL (#3221) by @ubkp +[build] ADDED: New BORDERLESS_WINDOWED_MODE for PLATFORM_DESKTOP (#3216) by @ubkp +[build] REVIEWED: Fix CMake extraneous -lglfw (#3266) by @iacore +[build] REVIEWED: Add missing cmake options (#3267) by @asdqwe +[build] REVIEWED: Match CMakeOptions.txt options default values (#3258) by @asdqwe +[build] REVIEWED: Add build.zig options for individual modules (#3254) by @actondev +[build] REVIEWED: build.zig to work with cross-compiling (#3225) by @yujiri8 +[build] REVIEWED: Makefile build on PLATFORM_ANDROID, soname (#3211) by @ndytts +[build] REVIEWED: src/Makefile, fix misleading indentation (#3202) by @ashn-dot-dev +[build] REVIEWED: build.zig: Support for building with PLAFORM_DRM (#3191) by @jakubvf +[build] REVIEWED: Update CMakeOptions.txt by @raysan5 +[build] REVIEWED: fix: cmake option "OPENGL_VERSION" doesn't work (#3170) by @royqh1979 +[build] REVIEWED: Add error if raylib.h is included in a C++98 program (#3093) by @Peter0x44 +[build] REVIEWED: Cross compilation for PLATFORM_DRM (#3091) by @TheLastBilly +[build] REVIEWED: build.zigm fixed cross-compiling from Linux (#3090)by @yujiri8 +[build] REVIEWED: Enhanced cmake part for OpenBSD (#3086) by @rayit +[build] REVIEWED: Fixed compile on OpenBSD (#3085)by @rayit +[build] REVIEWED: CMake project example: fix a couple of typos (#3014) by @benjamin-thomas +[build] REVIEWED: Fix warnings in raylib for MSVC (#3004) by @JeffM2501 +[build] REVIEWED: Update cmake example project (#3062) by @lesleyrs +[build] REVIEWED: Update build.zig be be able to build with current zig master (#3064) by @ryupold +[build] REVIEWED: VSCode project template (#3048) by @Shoozza +[build] REVIEWED: Fixed broken build.zig files. Now works with latest stable compiler (… by @Gamer-Kold +[build] REVIEWED: Fix missing symbol when rglfw.c on BSD platforms (#2968) by @Koromix +[build] REVIEWED: Update Makefile comment to indicate arm64 as a supported Linux deskto… @ashn-dot-dev +[build] REVIEWED: Update Makefile : clean raygui.c & physac.c (#3296) by @SuperUserNameMan +[build] REVIEWED: Update webassembly.yml and linux.yml +[build] REVIEWED: Update zig build system to zig version 0.11.0 (#3393) by @purple4pur +[build] REVIEWED: Fix for latest zig master (#3037) by @star-tek-mb +[bindings] ADDED: fortran-raylib +[bindings] ADDED: raylib-raku to bindings (#3299) by @vushu +[bindings] ADDED: claw-raylib to BINDINGS.md (#3310) by @bohonghuang +[bindings] ADDED: vaiorabbit/raylib-bindings (#3318) by @wilsonsilva +[bindings] ADDED: TurboRaylib (#3317) by @turborium +[bindings] ADDED: raylib-ffi to bindings list (#3164) by @ewpratten +[bindings] ADDED: raylib-pkpy-bindings (#3361) by @blueloveTH +[bindings] UPDATED: BINDINGS.md (#3217) by @joseph-montanez +[bindings] UPDATED: BINDINGS.md to include rayjs (#3212) by @mode777 +[bindings] UPDATED: latest h-raylib version (#3166) by @Anut-py +[bindings] UPDATED: bindbd-raylib3 to raylib 4.5 (#3157) by @o3o +[bindings] UPDATED: Janet bindings supported version update (#3083)by @archydragon +[bindings] UPDATED: BINDINGS.md (raylib-py -> 4.5) (#2992) by @overdev +[bindings] UPDATED: BINDINGS.md (raylib-lua -> 4.5) (#2989) by @TSnake41 +[bindings] UPDATED: raylib-d binding version to 4.5 (#2988) by @schveiguy +[bindings] UPDATED: raylib-freebasic to 4.5 (#2986) by @WIITD +[bindings] UPDATED: BINDINGS.md (#2983) by @jarroddavis68 +[bindings] UPDATED: BINDINGS.md for raylib Odin 4.5 (#2981) by @gingerBill +[bindings] UPDATED: BINDINGS.md (#2980) by @GuvaCode +[bindings] UPDATED: BINDINGS.md (#3002) by @fubark +[bindings] UPDATED: BINDINGS.md (#3053) by @JupiterRider +[bindings] UPDATED: BINDINGS.md (#3050) by @Its-Kenta +[bindings] UPDATED: CL bindings version (#3049) by @shelvick +[bindings] UPDATED: BINDINGS.md (#3026) by @ChrisDill +[bindings] UPDATED: BINDINGS.md (#3023) by @sDos280 +[bindings] UPDATED: BINDINGS.md (#3017) by @Soutaisei +[bindings] UPDATED: Various versions to 4.5 (#2974) by @RobLoach +[bindings] UPDATED: raylib.zig version to 4.5 (#2971) by @ryupold +[bindings] UPDATED: h-raylib version (#2970) by @Anut-py +[bindings] UPDATED: Factor's raylib binding to v4.5 (#3350) by @WraithGlade +[bindings] UPDATED: raylib-ocaml bindings to 4.5 version (#3322) by @tjammer +[external] UPDATED: sdefl and sinfl DEFLATE compression libraries by @raysan5 +[external] UPDATED: miniaudio v0.11.12 --> v0.11.18 by @raysan5 +[external] UPDATED: rl_gputex.h compressed images loading library by @raysan5 +[external] REVIEWED: msf_gif.h, some warnings +[misc] ADDED: New task point to issue template about checking the wiki (#3169) by @ubkp +[misc] REVIEWED: Update FAQ.md by @raysan5 +[misc] REVIEWED: Fix a link in the FAQ (#3082)by @jasonliang-dev +[misc] REVIEWED: New file formats to FAQ (#3079) by @Luramoth +[misc] REVIEWED: Make assets loading extension case insensitive #3008 by @raysan5 + ------------------------------------------------------------------------- Release: raylib 4.5 (18 March 2023) ------------------------------------------------------------------------- From f0124df0e8bbeb3e9ad2acf08b0f3610e812c8d6 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:34:24 +0200 Subject: [PATCH 0089/1037] Update CHANGELOG --- CHANGELOG | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index cad709bef..1da04cc73 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,7 @@ changelog Current Release: raylib 4.5.0 (18 March 2023) ------------------------------------------------------------------------- -Release: raylib 5.0 (xx November 2023) +Release: raylib 5.0 (xx November 2023) -WIP- ------------------------------------------------------------------------- KEY CHANGES: - REDESIGNED: rcore module split per-platform, by @ubkp, @michaelfiber, @Bigfoot71, @raysan5 @@ -19,8 +19,9 @@ Detailed changes: [rcore] ADDED: SetWindowTitle() for PLATFORM_WEB (#3222) by @VitusVeit [rcore] ADDED: FLAG_WINDOW_RESIZABLE for web (#3305) by @Peter0x44 [rcore] ADDED: SetWindowMaxSize() for desktop and web (#3309) by @ubkp +[rcore] ADDED: SetMouseCursor() for PLATFORM_WEB (#3414) by @BeardedBread [rcore] REMOVED: PLATFORM_RPI (#3232) by @michaelfiber -[rcore] REVIEWED: GetFileLength(), added comment #3262 by @raysan5 +[rcore] REVIEWED: GetFileLength(), added comment (#3262) by @raysan5 [rcore] REVIEWED: Default shaders precission issue on PLATFORM_WEB (#3261) by @branc116 [rcore] REVIEWED: IsKey*() key validation checks (#3256) by @n77y [rcore] REVIEWED: SetClipboardText() for PLATFORM_WEB (#3257) by @ubkp @@ -37,16 +38,17 @@ Detailed changes: [rcore] REVIEWED: Lazy loading of default font used on image drawing (no InitWindow) by @raysan5 [rcore] REVIEWED: Minor tweaks to raylib events automation system @raysan5 [rcore] REVIEWED: GetCurrentMonitor() bugfix (#3058) by @hamyyy -[rcore] REVIEWED: Update CORE.Input.Touch.pointCount #3024 by @raysan5 +[rcore] REVIEWED: Update CORE.Input.Touch.pointCount (#3024) by @raysan5 [rcore] REVIEWED: Mouse offset and scaling must be considered also on web! [rcore] REVIEWED: CompressData(), possible stack overflow [rcore] REVIEWED: GetWorldToScreenEx() (#3351) by @Brian-ED +[rcore] REVIEWED: Fix GetMouseDelta() issue for Android (#3404) by @Bigfoot71 [rlgl] ADDED: Experimental support for OpenGL ES 3.0 by @raysan5 [rlgl] ADDED: Support 16-Bit HDR textures (#3220) by @Not-Nik [rlgl] REVIEWED: Improved support for ES3/WebGL2 (#3107) by @chemaguerra [rlgl] REVIEWED: OpenGL 2.1 half floats support as part of an extension by @Not-Nik [rlgl] REVIEWED: Avoid shader attribute not found log by @raysan5 -[rlgl] REVIEWED: Avoid tracelog about not found uniforms #3003 by @raysan5 +[rlgl] REVIEWED: Avoid tracelog about not found uniforms (#3003) by @raysan5 [rlgl] REVIEWED: rLoadTexture() UBSAN complaints #1891 (#3321) by @Codom [rlgl] REVIEWED: glInternalFormat as unsigned int [rshapes] ADDED: Spline drawing functions by @raysan5 @@ -62,41 +64,45 @@ Detailed changes: [rtextures] ADDED: GenImageLinearGradient() by @danemadsen [rtextures] REMOVED: GenImageGradientH() and GenImageGradientV() by @danemadsen [rtextures] REVIEWED: LoadImageSvg() by @raysan5 -[rtextures] REVIEWED: Uninitialized thread-locals in stbi #3282 (#3283) by @jbarthelmes +[rtextures] REVIEWED: Uninitialized thread-locals in stbi (#3282) (#3283) by @jbarthelmes [rtextures] REVIEWED: ImageDrawRectangleRec(), validate drawing inside bounds by @JeffM2501 [rtextures] REVIEWED: LoadTextureCubemap() for manual layouts (#3204) by @Not-Nik [rtextures] REVIEWED: Optimization of ImageDrawRectangleRec() (#3185) by @smalltimewizard [rtextures] REVIEWED: ImageRotate() formatting by @raysan5 -[rtextures] REVIEWED: GenImagePerlinNoise(), clamp values #3071 by @raysan5 +[rtextures] REVIEWED: GenImagePerlinNoise(), clamp values (#3071) by @raysan5 [rtextures] REVIEWED: Packing logic error in GenImageFontAtlas() (#2979) by @hanaxar [rtextures] REVIEWED: Calculate exact image size in GenImageFontAtlas() (#2963) by @hanaxar -[rtextures] REVIEWED: ImageDrawRectangleRec() #3027 by @raysan5 +[rtextures] REVIEWED: ImageDrawRectangleRec() (#3027) by @raysan5 [rtextures] REVIEWED: ImageDraw() source clipping when drawing beyond top left (#3306) by @RobLoach [rtextures] REVIEWED: UnloadRenderTexture(), additional checks [rtext] ADDED: Font altas white rectangle and flag SUPPORT_FONT_ATLAS_WHITE_REC by @raysan5 [rtext] ADDED: SetTextLineSpacing() to define line breaks text drawing spacing by @raysan5 [rtext] RENAMED: LoadFont*() parameter names for consistency and coherence by @raysan5 [rtext] REVIEWED: GetCodepointCount(), ignore unused return value of GetCodepointNext by @ashn-dot-dev -[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured. (#3399) by @Murlocohol -[rtext] REVIEWED: GetGlyphIndex() #3000 by @raysan5 +[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured (#3399) by @Murlocohol +[rtext] REVIEWED: TextFormat(), added "..." for truncation (#3366) by @raysan5 +[rtext] REVIEWED: GetGlyphIndex() (#3000) by @raysan5 [rtext] REVIEWED: GetCodepointNext() to return default value on invalid inp… by @chocolate42 [rtext] REVIEWED: TextToPascal() issue when first char is uppercase [rmodels] ADDED: ModelAnimation.name field, initially with GLTF animation names loaded (… by @alfredbaudisch [rmodels] REVIEWED: Support .vox model files version 200 (#3097) by @Bigfoot71 -[rmodels] REVIEWED: Materials loading #3126 @raysan5 +[rmodels] REVIEWED: Materials loading (#3126) @raysan5 [rmodels] REVIEWED: DrawBillboardPro() to allow source of negative size (#3197) by @bohonghuang [rmodels] REVIEWED: glTF loading segfault in animNormals memcpy by @charles-l [rmodels] REVIEWED: LoadModelAnimationsGLTF(), free fileData after use (#3065) by @crynux [rmodels] REVIEWED: GenMeshCubicmap(), correction of values (#3032) by @Bigfoot71 [rmodels] REVIEWED: DrawMesh() to avoid UBSAN complaining (#1891) +[rmodels] REVIEWED: GenMeshPlane() when resX != resZ (#3425) by @neyrox, @s-yablonskiy [raudio] ADDED: LoadSoundAlias() by @JeffM2501 -[raudio] ADDED: Missing structure on standalone mode #3160 by @raysan5 +[raudio] ADDED: Missing structure on standalone mode (#3160) by @raysan5 +[raudio] ADDED: GetMasterVolume() (#3434) by @rexim [raudio] REVIEWED: Comments about sample format to AttachAudioStreamProcessor() (#3188) by @AlbertoGP [raudio] REVIEWED: Documented buffer format for audio processors (#3186) by @AlbertoGP [raudio] REVIEWED: ExportWaveAsCode() file saving by @RadsammyT [raudio] REVIEWED: Fix warning on discarded const qualifier (#2967) by @RobLoach [raudio] REVIEWED: Move mutex initialization before ma_device_start() (#3325) by @Bigfoot71 -[rcamera] REVIEWED: File-macros for consistency #3161 by @raysan5 +[raudio] REVIEWED: Fix UpdateSound() parameter name (#3405) by @KislyjKisel +[rcamera] REVIEWED: File-macros for consistency (#3161) by @raysan5 [rcamera] REVIEWED: Support analog stick camera controls (#3066) by @PixelPhobicGames [rcamera] REVIEWED: CameraMoveToTarget(), ensure distance is greater than 0 (#3031) by @kolunmi [rcamera] REVIEWED: Exposing rcamera functions to the dll (#3355) by @JeffM2501 @@ -159,6 +165,7 @@ Detailed changes: [build] REVIEWED: Update webassembly.yml and linux.yml [build] REVIEWED: Update zig build system to zig version 0.11.0 (#3393) by @purple4pur [build] REVIEWED: Fix for latest zig master (#3037) by @star-tek-mb +[build] REVIEWED: Examples Makefile to use Makefile.Web when building for web (#3449) by @keithstellyes [bindings] ADDED: fortran-raylib [bindings] ADDED: raylib-raku to bindings (#3299) by @vushu [bindings] ADDED: claw-raylib to BINDINGS.md (#3310) by @bohonghuang @@ -166,6 +173,7 @@ Detailed changes: [bindings] ADDED: TurboRaylib (#3317) by @turborium [bindings] ADDED: raylib-ffi to bindings list (#3164) by @ewpratten [bindings] ADDED: raylib-pkpy-bindings (#3361) by @blueloveTH +[bindings] ADDED: Raylib.lean to BINDINGS.md (#3409) by @KislyjKisel [bindings] UPDATED: BINDINGS.md (#3217) by @joseph-montanez [bindings] UPDATED: BINDINGS.md to include rayjs (#3212) by @mode777 [bindings] UPDATED: latest h-raylib version (#3166) by @Anut-py @@ -193,6 +201,7 @@ Detailed changes: [external] UPDATED: sdefl and sinfl DEFLATE compression libraries by @raysan5 [external] UPDATED: miniaudio v0.11.12 --> v0.11.18 by @raysan5 [external] UPDATED: rl_gputex.h compressed images loading library by @raysan5 +[external] UPDATED: Replaced stb_image_resize.c by stb_image_resize2.h (#3403) by @BabakSamimi [external] REVIEWED: msf_gif.h, some warnings [misc] ADDED: New task point to issue template about checking the wiki (#3169) by @ubkp [misc] REVIEWED: Update FAQ.md by @raysan5 From e33e9da277865207123158430ebf42cc5626e5b7 Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Sun, 22 Oct 2023 16:13:49 +0100 Subject: [PATCH 0090/1037] Add DrawCircleLinesV for consistency (#3452) ImageDrawCircleLinesV already existed, so I'm not sure why this was missing. It is trivial to implement, anyway --- src/raylib.h | 1 + src/rshapes.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index e701f0454..b172562d0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1207,6 +1207,7 @@ RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline +RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version) RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring diff --git a/src/rshapes.c b/src/rshapes.c index f3061f8b3..de64f1593 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -670,6 +670,12 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) +{ + DrawCircleLinesV((Vector2){ (float)centerX, (float)centerY }, radius, color); +} + +// Draw circle outline (Vector version) +void DrawCircleLinesV(Vector2 center, float radius, Color color) { rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -677,8 +683,8 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) for (int i = 0; i < 360; i += 10) { - rlVertex2f(centerX + cosf(DEG2RAD*i)*radius, centerY + sinf(DEG2RAD*i)*radius); - rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radius, centerY + sinf(DEG2RAD*(i + 10))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius); } rlEnd(); } From b3028e4891083c077236df437011184b16b6d293 Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Sun, 22 Oct 2023 18:45:49 +0100 Subject: [PATCH 0091/1037] Review prerequisites of rcore.c (#3453) rcore_desktop_sdl.c was not present in the list of prerequisites this patch changes them to use a wildcard, so any other platforms added in future will be tracked properly --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index ea8bdd5e5..c9094f096 100644 --- a/src/Makefile +++ b/src/Makefile @@ -633,7 +633,7 @@ endif # Compile all modules with their prerequisites # Prerequisites of core module -rcore.o : platforms/rcore_android.c platforms/rcore_desktop.c platforms/rcore_drm.c platforms/rcore_template.c platforms/rcore_web.c +rcore.o : platforms/*.c # Compile core module rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h From 8f517b76516c207c89cabf055fa7db768ba6d960 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 23 Oct 2023 05:05:40 -0300 Subject: [PATCH 0092/1037] Fix compilation for PLATFORM_WEB examples (#3454) --- examples/Makefile.Web | 60 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index ac8d5af89..3512f7407 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -149,7 +149,7 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) MAKE = mingw32-make endif ifeq ($(PLATFORM),PLATFORM_WEB) - MAKE = mingw32-make + MAKE = emmake make endif # Define compiler flags: CFLAGS @@ -268,7 +268,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # are specified per-example for optimization # Define a custom shell .html and output extension - LDFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html + LDFLAGS += --shell-file $(RAYLIB_PATH)/src/minshell.html EXT = .html endif @@ -340,6 +340,7 @@ CORE = \ core/core_input_mouse \ core/core_input_mouse_wheel \ core/core_input_gamepad \ + core/core_input_gamepad_info \ core/core_input_multitouch \ core/core_input_gestures \ core/core_input_gestures_web \ @@ -392,6 +393,7 @@ TEXTURES = \ textures/textures_image_generation \ textures/textures_image_loading \ textures/textures_image_processing \ + textures/textures_image_rotate \ textures/textures_image_text \ textures/textures_to_image \ textures/textures_raw_data \ @@ -473,9 +475,18 @@ AUDIO = \ audio/audio_music_stream \ audio/audio_raw_stream \ audio/audio_sound_loading \ + audio/audio_sound_multi \ audio/audio_stream_effects \ audio/audio_mixed_processor +OTHERS = \ + others/easings_testbed \ + others/embedded_files_loading \ + others/raylib_opengl_interop \ + others/raymath_vector_angle \ + others/rlgl_compute_shader \ + others/rlgl_standalone + CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) # Define processes to execute @@ -512,6 +523,9 @@ core/core_input_gamepad: core/core_input_gamepad.c --preload-file core/resources/ps3.png@resources/ps3.png \ --preload-file core/resources/xbox.png@resources/xbox.png +core/core_input_gamepad_info: core/core_input_gamepad.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_input_multitouch: core/core_input_multitouch.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -529,7 +543,7 @@ core/core_2d_camera_platformer: core/core_2d_camera_platformer.c core/core_2d_camera_mouse_zoom: core/core_2d_camera_mouse_zoom.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + core/core_2d_camera_split_screen: core/core_2d_camera_split_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -541,7 +555,7 @@ core/core_3d_camera_free: core/core_3d_camera_free.c core/core_3d_camera_first_person: core/core_3d_camera_first_person.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -675,6 +689,10 @@ textures/textures_image_processing: textures/textures_image_processing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/parrots.png@resources/parrots.png +textures/textures_image_rotate: textures/textures_image_rotate.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file textures/resources/raylib_logo.png + textures/textures_image_text: textures/textures_image_text.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file textures/resources/parrots.png@resources/parrots.png \ @@ -740,6 +758,10 @@ textures/textures_gif_player: textures/textures_gif_player.c textures/textures_fog_of_war: textures/textures_fog_of_war.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +textures/textures_svg_loading: textures/textures_svg_loading.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file textures/resources/test.svg + # Compile TEXT examples text/text_raylib_fonts: text/text_raylib_fonts.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -963,6 +985,13 @@ shaders/shaders_hot_reloading: shaders/shaders_hot_reloading.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s FORCE_FILESYSTEM=1 \ --preload-file shaders/resources/shaders/glsl100/reload.fs@resources/shaders/glsl100/reload.fs +shaders/shaders_lightmap: shaders/shaders_lightmap.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s FORCE_FILESYSTEM=1 \ + --preload-file shaders/resources/shaders/glsl330/lightmap.vs \ + --preload-file shaders/resources/shaders/glsl330/lightmap.fs \ + --preload-file shaders/resources/cubicmap_atlas.png \ + --preload-file shaders/resources/spark_flame.png + shaders/shaders_mesh_instancing: shaders/shaders_mesh_instancing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/lighting_instancing.vs@resources/shaders/glsl100/lighting_instancing.vs \ @@ -1003,6 +1032,10 @@ audio/audio_sound_loading: audio/audio_sound_loading.c --preload-file audio/resources/sound.wav@resources/sound.wav \ --preload-file audio/resources/target.ogg@resources/target.ogg +audio/audio_sound_multi: audio/audio_sound_multi.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file audio/resources/sound.wav@resources/sound.wav + audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 @@ -1012,6 +1045,25 @@ audio/audio_mixed_processor: audio/audio_mixed_processor.c --preload-file audio/resources/country.mp3@resources/country.mp3 \ --preload-file audio/resources/coin.wav@resources/coin.wav +# Compile OTHERS examples +others/easings_testbed: others/easings_testbed.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +others/embedded_files_loading: others/embedded_files_loading.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +others/raylib_opengl_interop: + $(info Skipping_others_raylib_opengl_interop) + +others/raymath_vector_angle: others/raymath_vector_angle.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +others/rlgl_compute_shader: + $(info Skipping_others_rlgl_compute_shader) + +others/rlgl_standalone: + $(info Skipping_others_rlgl_standalone) + # Clean everything clean: ifeq ($(PLATFORM),PLATFORM_DESKTOP) From 4ed776368a488b242781d77753f4b11396f97ce7 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Mon, 23 Oct 2023 01:11:50 -0700 Subject: [PATCH 0093/1037] When the frame counter gets to 0, reset the FPS average counter. This allows the window to be closed and reopened with clean FPS stats. (#3445) --- src/rcore.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index e3cd08188..9bd47983a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -484,9 +484,9 @@ void InitWindow(int width, int height, const char *title) } #endif + CORE.Time.frameCounter = 0; #if defined(SUPPORT_EVENTS_AUTOMATION) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; #endif // Initialize random seed @@ -1396,6 +1396,16 @@ int GetFPS(void) static float average = 0, last = 0; float fpsFrame = GetFrameTime(); + // if we reset the window, reset the FPS info + if (CORE.Time.frameCounter == 0) + { + average = 0; + last = 0; + index = 0; + for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) + history[i] = 0; + } + if (fpsFrame == 0) return 0; if ((GetTime() - last) > FPS_STEP) From daf227a185808d1e37e4269cf4a5be6b690226f6 Mon Sep 17 00:00:00 2001 From: Lukas <116672956+gk646@users.noreply.github.com> Date: Mon, 23 Oct 2023 18:16:28 +0200 Subject: [PATCH 0094/1037] Fixes a memory leak as a result of creating an AudioBuffer* with the old source.frameCount. This internally allocates memory to the structs data pointer which is then later overridden by the correct sound data of the source sound. (#3458) Additionally added a volume assignment from old to new as currently there is no way to get the volume of a sound and the AudioBuffer struct is not reachable from user code due to opaque definition. --- src/raudio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 6a1096767..dcc9f706a 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -928,7 +928,6 @@ Sound LoadSoundFromWave(Wave wave) } // Clone sound from existing sound data, clone does not own wave data -// Wave data must // NOTE: Wave data must be unallocated manually and will be shared across all clones Sound LoadSoundAlias(Sound source) { @@ -936,13 +935,16 @@ Sound LoadSoundAlias(Sound source) if (source.stream.buffer->data != NULL) { - AudioBuffer* audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, source.frameCount, AUDIO_BUFFER_USAGE_STATIC); + AudioBuffer* audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); if (audioBuffer == NULL) { TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); return sound; // early return to avoid dereferencing the audioBuffer null pointer } + audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames; + audioBuffer->volume = source.stream.buffer->volume; audioBuffer->data = source.stream.buffer->data; + sound.frameCount = source.frameCount; sound.stream.sampleRate = AUDIO.System.device.sampleRate; sound.stream.sampleSize = 32; @@ -953,6 +955,7 @@ Sound LoadSoundAlias(Sound source) return sound; } + // Checks if a sound is ready bool IsSoundReady(Sound sound) { From 3ff60269174d0f264c152875be4d1808b7fe0195 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Oct 2023 18:32:24 +0200 Subject: [PATCH 0095/1037] REVIEWED: Move screen capture logic to `rcore.c`, available for all platforms --- src/platforms/rcore_desktop.c | 55 -------------------------------- src/platforms/rcore_web.c | 59 ----------------------------------- src/rcore.c | 40 +++++++++++++++++++++++- 3 files changed, 39 insertions(+), 115 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 9e653dd0e..b7def6e67 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1658,61 +1658,6 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // Check the exit key to set close window if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); - -#if defined(SUPPORT_SCREEN_CAPTURE) - if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) - { -#if defined(SUPPORT_GIF_RECORDING) - if (mods & GLFW_MOD_CONTROL) - { - if (gifRecording) - { - gifRecording = false; - - MsfGifResult result = msf_gif_end(&gifState); - - SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); - msf_gif_free(result); - - TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); - } - else - { - gifRecording = true; - gifFrameCounter = 0; - - Vector2 scale = GetWindowScaleDPI(); - msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - screenshotCounter++; - - TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); - } - } - else -#endif // SUPPORT_GIF_RECORDING - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - } -#endif // SUPPORT_SCREEN_CAPTURE - -#if defined(SUPPORT_EVENTS_AUTOMATION) - if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) - { - eventsRecording = !eventsRecording; - - // On finish recording, we export events into a file - if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); - } - else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) - { - LoadAutomationEvents("eventsrec.rep"); - eventsPlaying = true; - - TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); - } -#endif } // GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index e918e7d34..7943e18f8 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1044,65 +1044,6 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // Check the exit key to set close window if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); - -#if defined(SUPPORT_SCREEN_CAPTURE) - if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) - { -#if defined(SUPPORT_GIF_RECORDING) - if (mods & GLFW_MOD_CONTROL) - { - if (gifRecording) - { - gifRecording = false; - - MsfGifResult result = msf_gif_end(&gifState); - - SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); - msf_gif_free(result); - - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); - - TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); - } - else - { - gifRecording = true; - gifFrameCounter = 0; - - Vector2 scale = GetWindowScaleDPI(); - msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - screenshotCounter++; - - TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); - } - } - else -#endif // SUPPORT_GIF_RECORDING - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - } -#endif // SUPPORT_SCREEN_CAPTURE - -#if defined(SUPPORT_EVENTS_AUTOMATION) - if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) - { - eventsRecording = !eventsRecording; - - // On finish recording, we export events into a file - if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); - } - else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) - { - LoadAutomationEvents("eventsrec.rep"); - eventsPlaying = true; - - TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); - } -#endif } // GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) diff --git a/src/rcore.c b/src/rcore.c index 9bd47983a..0cd3f4408 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -739,6 +739,44 @@ void EndDrawing(void) PollInputEvents(); // Poll user events (before next frame update) #endif +#if defined(SUPPORT_SCREEN_CAPTURE) + if (IsKeyPressed(KEY_F12)) + { +#if defined(SUPPORT_GIF_RECORDING) + if (IsKeyDown(KEY_LEFT_CONTROL)) + { + if (gifRecording) + { + gifRecording = false; + + MsfGifResult result = msf_gif_end(&gifState); + + SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); + msf_gif_free(result); + + TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); + } + else + { + gifRecording = true; + gifFrameCounter = 0; + + Vector2 scale = GetWindowScaleDPI(); + msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + screenshotCounter++; + + TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); + } + } + else +#endif // SUPPORT_GIF_RECORDING + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + } +#endif // SUPPORT_SCREEN_CAPTURE + #if defined(SUPPORT_EVENTS_AUTOMATION) // Events recording and playing logic if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter); @@ -748,7 +786,7 @@ void EndDrawing(void) if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false; PlayAutomationEvent(CORE.Time.frameCounter); } -#endif +#endif // SUPPORT_EVENTS_AUTOMATION CORE.Time.frameCounter++; } From a0f00343523a8898dfed46ee21afaa99d0c15f66 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Oct 2023 19:15:40 +0200 Subject: [PATCH 0096/1037] REVIEWED: `InitPlatform()` organization and code-gardening --- src/platforms/rcore_android.c | 39 +++++----- src/platforms/rcore_desktop.c | 115 ++++++++++++++++++------------ src/platforms/rcore_desktop_sdl.c | 34 ++++++--- src/platforms/rcore_drm.c | 56 +++++++++------ src/platforms/rcore_template.c | 30 ++++++-- src/platforms/rcore_web.c | 79 ++++++++++++-------- src/rcore.c | 2 - 7 files changed, 221 insertions(+), 134 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 1272eecc3..4faf73b7d 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -518,6 +518,8 @@ void PollInputEvents(void) // Initialize platform: graphics, inputs and more int InitPlatform(void) { + // Initialize display basic configuration + //---------------------------------------------------------------------------- CORE.Window.currentFbo.width = CORE.Window.screen.width; CORE.Window.currentFbo.height = CORE.Window.screen.height; @@ -545,27 +547,33 @@ int InitPlatform(void) //AConfiguration_getKeyboard(platform.app->config); //AConfiguration_getScreenSize(platform.app->config); //AConfiguration_getScreenLong(platform.app->config); - - // Initialize App command system - // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - platform.app->onAppCmd = AndroidCommandCallback; - - // Initialize input events system - platform.app->onInputEvent = AndroidInputCallback; - - // Initialize assets manager - InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); - - // Initialize base path for storage - CORE.Storage.basePath = platform.app->activity->internalDataPath; - + // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false + //---------------------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); + // Initialize App command system + // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... + //---------------------------------------------------------------------------- + platform.app->onAppCmd = AndroidCommandCallback; + //---------------------------------------------------------------------------- + + // Initialize input events system + //---------------------------------------------------------------------------- + platform.app->onInputEvent = AndroidInputCallback; + //---------------------------------------------------------------------------- + + // Initialize storage system + //---------------------------------------------------------------------------- + InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize assets manager + + CORE.Storage.basePath = platform.app->activity->internalDataPath; // Define base path for storage + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Initialized successfully"); // Android ALooper_pollAll() variables int pollResult = 0; @@ -613,7 +621,6 @@ void ClosePlatform(void) } } - // Initialize display device and framebuffer // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index b7def6e67..f1ad8a9a3 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1236,6 +1236,8 @@ int InitPlatform(void) int result = glfwInit(); if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- glfwDefaultWindowHints(); // Set default windows hints //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits @@ -1450,60 +1452,73 @@ int InitPlatform(void) } glfwMakeContextCurrent(platform.handle); - glfwSwapInterval(0); // No V-Sync by default - - // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) - // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need - // to be activated on web platforms since VSync is enforced there. - if (CORE.Window.flags & FLAG_VSYNC_HINT) + result = glfwGetError(NULL); + + // Check context activation + if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { - // WARNING: It seems to hit a critical render path in Intel HD Graphics - glfwSwapInterval(1); - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); + CORE.Window.ready = true; + + glfwSwapInterval(0); // No V-Sync by default + + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need + // to be activated on web platforms since VSync is enforced there. + if (CORE.Window.flags & FLAG_VSYNC_HINT) + { + // WARNING: It seems to hit a critical render path in Intel HD Graphics + glfwSwapInterval(1); + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); + } + + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. + // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); + #if !defined(__APPLE__) + glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); + + // Screen scaling matrix is required in case desired screen area is different from display area + CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); + + // Mouse input scaling for the new screen size + SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); + #endif + } + + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + CORE.Window.currentFbo.width = fbWidth; + CORE.Window.currentFbo.height = fbHeight; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - - int fbWidth = CORE.Window.screen.width; - int fbHeight = CORE.Window.screen.height; - - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. - // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); -#if !defined(__APPLE__) - glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); - - // Screen scaling matrix is required in case desired screen area is different from display area - CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); - - // Mouse input scaling for the new screen size - SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); -#endif + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; } - CORE.Window.render.width = fbWidth; - CORE.Window.render.height = fbHeight; - CORE.Window.currentFbo.width = fbWidth; - CORE.Window.currentFbo.height = fbHeight; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + //---------------------------------------------------------------------------- + // Initialize input events callbacks + //---------------------------------------------------------------------------- // Set window callback events glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); @@ -1521,12 +1536,19 @@ int InitPlatform(void) glfwSetJoystickCallback(JoystickCallback); glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + //---------------------------------------------------------------------------- - // Initialize hi-res timer + // Initialize timming system + //---------------------------------------------------------------------------- InitTimer(); - - // Initialize base path for storage + //---------------------------------------------------------------------------- + + // Initialize storage system + //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully"); return 0; } @@ -1542,7 +1564,6 @@ void ClosePlatform(void) #endif } - // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index d2f550ace..7fe9673fa 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1107,6 +1107,8 @@ int InitPlatform(void) int result = SDL_Init(SDL_INIT_EVERYTHING); if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; } + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- unsigned int flags = 0; flags |= SDL_WINDOW_SHOWN; flags |= SDL_WINDOW_OPENGL; @@ -1143,6 +1145,7 @@ int InitPlatform(void) //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; // NOTE: Some OpenGL context attributes must be set before window creation + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); @@ -1170,7 +1173,7 @@ int InitPlatform(void) { CORE.Window.ready = true; - SDL_DisplayMode displayMode; + SDL_DisplayMode displayMode = { 0 }; SDL_GetCurrentDisplayMode(GetCurrentMonitor(), &displayMode); CORE.Window.display.width = displayMode.w; @@ -1187,30 +1190,43 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; + } // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(SDL_GL_GetProcAddress); + //---------------------------------------------------------------------------- - - // Init input gamepad + // Initialize input events system + //---------------------------------------------------------------------------- if (SDL_NumJoysticks() >= 1) { SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } + //---------------------------------------------------------------------------- - // Initialize hi-res timer - //InitTimer(); + // Initialize timming system + //---------------------------------------------------------------------------- + // NOTE: No need to call InitTimer(), let SDL manage it internally CORE.Time.previous = GetTime(); // Get time as double + //---------------------------------------------------------------------------- - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); + // Initialize storage system + //---------------------------------------------------------------------------- + CORE.Storage.basePath = GetWorkingDirectory(); // Define base path for storage + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully"); return 0; } +// Close platform void ClosePlatform(void) { SDL_FreeCursor(platform.cursor); // Free cursor @@ -1219,7 +1235,7 @@ void ClosePlatform(void) SDL_Quit(); // Deinitialize SDL internal global state } - +// Scancode to keycode mapping static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) { if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 2ecfc2a3a..9609c50d2 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -576,6 +576,8 @@ int InitPlatform(void) platform.prevBO = NULL; platform.prevFB = 0; + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -846,7 +848,6 @@ int InitPlatform(void) } // Create an EGL window surface - //--------------------------------------------------------------------------------- platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL); if (EGL_NO_SURFACE == platform.surface) { @@ -863,14 +864,14 @@ int InitPlatform(void) // There must be at least one frame displayed before the buffers are swapped //eglSwapInterval(platform.device, 1); + + EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); - if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return -1; - } - else + // Check surface and context activation + if (result != EGL_FALSE) { + CORE.Window.ready = true; + CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; @@ -882,16 +883,15 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(eglGetProcAddress); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; + } if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - - // If graphic device is no properly initialized, we end program + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); @@ -901,16 +901,29 @@ int InitPlatform(void) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false - // Initialize hi-res timer + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + //---------------------------------------------------------------------------- + + // Initialize input events system + //---------------------------------------------------------------------------- + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + //---------------------------------------------------------------------------- + + // Initialize timming system + //---------------------------------------------------------------------------- InitTimer(); + //---------------------------------------------------------------------------- - // Initialize base path for storage + // Initialize storage system + //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); - - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); return 0; } @@ -1005,7 +1018,6 @@ void ClosePlatform(void) if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); } - // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 08210289e..11ce45c1e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -518,13 +518,27 @@ int InitPlatform(void) EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); - // Enabling current display surface and context failed - if (result == EGL_FALSE) + // Check surface and context activation + if (result != EGL_FALSE) { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + CORE.Window.ready = true; + + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } - else CORE.Window.ready = true; //---------------------------------------------------------------------------- // If everything work as expected, we can continue @@ -545,7 +559,7 @@ int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - // TODO: Initialize input system + // TODO: Initialize input events system // It could imply keyboard, mouse, gamepad, touch... // Depending on the platform libraries/SDK it could use a callbacks mechanims // For system events and inputs evens polling on a per-frame basis, use PollInputEvents() @@ -553,15 +567,17 @@ int InitPlatform(void) // ... //---------------------------------------------------------------------------- - // TODO: Initialize hi-res timer + // TODO: Initialize timming system //---------------------------------------------------------------------------- InitTimer(); //---------------------------------------------------------------------------- - // TODO: Initialize base path for storage + // TODO: Initialize storage system //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully"); return 0; } diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 7943e18f8..3da23a054 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -677,6 +677,8 @@ int InitPlatform(void) int result = glfwInit(); if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- glfwDefaultWindowHints(); // Set default windows hints // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits // glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits @@ -862,43 +864,46 @@ int InitPlatform(void) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); + result = glfwGetError(NULL); + + // Check context activation + if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) + { + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + CORE.Window.currentFbo.width = fbWidth; + CORE.Window.currentFbo.height = fbHeight; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; + } if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) - // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need - // to be activated on web platforms since VSync is enforced there. - - int fbWidth = CORE.Window.screen.width; - int fbHeight = CORE.Window.screen.height; - - CORE.Window.render.width = fbWidth; - CORE.Window.render.height = fbHeight; - CORE.Window.currentFbo.width = fbWidth; - CORE.Window.currentFbo.height = fbHeight; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + //---------------------------------------------------------------------------- + + // Initialize input events callbacks + //---------------------------------------------------------------------------- // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); @@ -927,6 +932,19 @@ int InitPlatform(void) // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + //---------------------------------------------------------------------------- + + // Initialize timming system + //---------------------------------------------------------------------------- + InitTimer(); + //---------------------------------------------------------------------------- + + // Initialize storage system + //---------------------------------------------------------------------------- + CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: WEB: Initialized successfully"); return 0; } @@ -938,7 +956,6 @@ void ClosePlatform(void) glfwTerminate(); } - // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/rcore.c b/src/rcore.c index 0cd3f4408..ef3beaee3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -491,8 +491,6 @@ void InitWindow(int width, int height, const char *title) // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); } // Close window and unload OpenGL context From 803b1a910e0b23986688e9d4e53b77d18ba41767 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Oct 2023 19:59:29 +0200 Subject: [PATCH 0097/1037] REVIEWED: Check OpenGL version required, fix #3457 --- src/platforms/rcore_desktop_sdl.c | 43 +++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 7fe9673fa..ad051a95b 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1145,11 +1145,44 @@ int InitPlatform(void) //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; // NOTE: Some OpenGL context attributes must be set before window creation - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + } + else if (rlGetVersion() == RL_OPENGL_33) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); +#if defined(__APPLE__) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // OSX Requires forward compatibility +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#endif + } + else if (rlGetVersion() == RL_OPENGL_43) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); // Enable OpenGL Debug Context +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } if (CORE.Window.flags & FLAG_VSYNC_HINT) { From 8fbd42d592c22612e18d2c6f9bcef8a107984675 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 25 Oct 2023 10:14:17 +0200 Subject: [PATCH 0098/1037] Fix #3461 --- src/platforms/rcore_desktop.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index f1ad8a9a3..de34c8711 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1120,7 +1120,11 @@ void PollInputEvents(void) // NOTE: We do it here in case of disconnection for (int i = 0; i < MAX_GAMEPADS; i++) { - if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; + if (glfwJoystickPresent(i)) + { + CORE.Input.Gamepad.ready[i] = true; + strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); + } else CORE.Input.Gamepad.ready[i] = false; } From 7e5eff8a29525df247110268133dcf11f9e72b11 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 25 Oct 2023 10:15:19 +0200 Subject: [PATCH 0099/1037] Revert "Fix #3461" This reverts commit 8fbd42d592c22612e18d2c6f9bcef8a107984675. --- src/platforms/rcore_desktop.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index de34c8711..f1ad8a9a3 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1120,11 +1120,7 @@ void PollInputEvents(void) // NOTE: We do it here in case of disconnection for (int i = 0; i < MAX_GAMEPADS; i++) { - if (glfwJoystickPresent(i)) - { - CORE.Input.Gamepad.ready[i] = true; - strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); - } + if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; else CORE.Input.Gamepad.ready[i] = false; } From b0c0f2e5606f129175a10919a16b9eaea248f150 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 25 Oct 2023 07:17:54 -0300 Subject: [PATCH 0100/1037] Fix OpenURL on SDL (#3460) --- src/platforms/rcore_desktop_sdl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index ad051a95b..ccc2acf0a 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -893,9 +893,15 @@ double GetTime(void) } // Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - SDL_OpenURL(url); + // Security check to (partially) avoid malicious code + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else SDL_OpenURL(url); } //---------------------------------------------------------------------------------- @@ -1145,7 +1151,7 @@ int InitPlatform(void) //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; // NOTE: Some OpenGL context attributes must be set before window creation - + // Check selection OpenGL version if (rlGetVersion() == RL_OPENGL_21) { @@ -1224,9 +1230,9 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); - return -1; + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; } // Load OpenGL extensions @@ -1253,7 +1259,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); // Define base path for storage //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully"); return 0; From cb1c2ffda133a43b6dd7237c9139110954712d75 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:13:51 -0300 Subject: [PATCH 0101/1037] Fix gamepad names for PLATFORM_DESKTOP/GLFW (#3462) --- src/platforms/rcore_desktop.c | 36 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index f1ad8a9a3..b43a6b5ae 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -649,12 +649,12 @@ void SetWindowMinSize(int width, int height) { CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; - + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; - + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } @@ -663,12 +663,12 @@ void SetWindowMaxSize(int width, int height) { CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; - + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; - + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } @@ -1108,7 +1108,7 @@ void PollInputEvents(void) // Reset touch positions //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; - + // Map touch position to mouse position for convenience // WARNING: If the target desktop device supports touch screen, this behavious should be reviewed! // TODO: GLFW does not support multi-touch input just yet @@ -1406,7 +1406,7 @@ int InitPlatform(void) } } } - + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, @@ -1450,10 +1450,10 @@ int InitPlatform(void) TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); return -1; } - + glfwMakeContextCurrent(platform.handle); result = glfwGetError(NULL); - + // Check context activation if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { @@ -1500,9 +1500,9 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } @@ -1511,12 +1511,12 @@ int InitPlatform(void) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(glfwGetProcAddress); //---------------------------------------------------------------------------- - + // Initialize input events callbacks //---------------------------------------------------------------------------- // Set window callback events @@ -1536,6 +1536,12 @@ int InitPlatform(void) glfwSetJoystickCallback(JoystickCallback); glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + + // Retrieve gamepad names + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (glfwJoystickPresent(i)) strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); + } //---------------------------------------------------------------------------- // Initialize timming system @@ -1547,9 +1553,9 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully"); - + return 0; } From 9a687e3153633479067fbb0439a7e0214dec665b Mon Sep 17 00:00:00 2001 From: 2Bear Date: Thu, 26 Oct 2023 16:15:25 +0800 Subject: [PATCH 0102/1037] Fix missing `PLATFORM_DESKTOP_SDL` checks. (#3469) --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index e38642ec9..7c93bf929 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -803,7 +803,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 - #if defined(PLATFORM_DESKTOP) + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) #define GLAD_GLES2_IMPLEMENTATION #include "external/glad_gles2.h" #else @@ -2248,7 +2248,7 @@ void rlLoadExtensions(void *loader) // RLGL.ExtSupported.maxAnisotropyLevel #elif defined(GRAPHICS_API_OPENGL_ES2) - #if defined(PLATFORM_DESKTOP) + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) // TODO: Support OpenGL ES 3.0 if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); From 2f6b2897fe9d6a777b4f32ff6490436fbbb1b54b Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Thu, 26 Oct 2023 05:18:00 -0300 Subject: [PATCH 0103/1037] GetCurrentMonitor() - check window center instead of top-left corner (#3468) --- src/platforms/rcore_desktop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index b43a6b5ae..1114181a4 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -755,6 +755,8 @@ int GetCurrentMonitor(void) int y = 0; glfwGetWindowPos(platform.handle, &x, &y); + x += (int)CORE.Window.screen.width / 2; + y += (int)CORE.Window.screen.height / 2; for (int i = 0; i < monitorCount; i++) { From 804f1a83eba7aa5cbb701457e8d6cda372b1a10d Mon Sep 17 00:00:00 2001 From: jestarray <34615798+jestarray@users.noreply.github.com> Date: Thu, 26 Oct 2023 01:24:21 -0700 Subject: [PATCH 0104/1037] Fix IsGestureDetected parameter inconsistency in raylib.h with rgextures.h (#3464) closes https://github.com/raysan5/raylib/issues/3463 --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index b172562d0..40fcd98fd 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1167,7 +1167,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected +RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected RLAPI int GetGestureDetected(void); // Get latest detected gesture RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector From eddeafd2ed3be0b738a9ebcb5083448bd9640542 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 10:28:00 +0200 Subject: [PATCH 0105/1037] Revert "Fix IsGestureDetected parameter inconsistency in raylib.h with rgextures.h (#3464)" This reverts commit 804f1a83eba7aa5cbb701457e8d6cda372b1a10d. --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 40fcd98fd..b172562d0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1167,7 +1167,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected +RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected RLAPI int GetGestureDetected(void); // Get latest detected gesture RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector From 77730c80d903083751cc81a6912593c95f4f7165 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 10:34:39 +0200 Subject: [PATCH 0106/1037] Updated to miniaudio v0.11.19 #3448 --- src/external/miniaudio.h | 168 +++++++++++++++++++++++++-------------- 1 file changed, 110 insertions(+), 58 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 181f45289..518e3c43a 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.18 - 2023-08-07 +miniaudio - v0.11.19 - TBD David Reid - mackron@gmail.com @@ -87,7 +87,7 @@ device on the stack, but you could allocate it on the heap if that suits your si // Do something here. Probably your program's main loop. - ma_device_uninit(&device); // This will stop the device so no need to do that manually. + ma_device_uninit(&device); return 0; } ``` @@ -1675,7 +1675,7 @@ an example for initializing a data source: // ... - ma_resource_manager_data_source_uninit(pResourceManager, &dataSource); + ma_resource_manager_data_source_uninit(&dataSource); ``` The `flags` parameter specifies how you want to perform loading of the sound file. It can be a @@ -1912,10 +1912,10 @@ once after the other: ```c ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. - ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded. + ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. - ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded. + ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. ``` A binary search tree (BST) is used for storing data buffers as it has good balance between @@ -3409,7 +3409,7 @@ miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buf read from memory that's managed by the application, but can also handle the memory management for you internally. Memory management is flexible and should support most use cases. -Audio buffers are initialised using the standard configuration system used everywhere in miniaudio: +Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: ```c ma_audio_buffer_config config = ma_audio_buffer_config_init( @@ -3716,7 +3716,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 18 +#define MA_VERSION_REVISION 19 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -4267,7 +4267,7 @@ typedef enum ma_standard_sample_rate_192000 = 192000, ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ - ma_standard_sample_rate_11025 = 11250, + ma_standard_sample_rate_11025 = 11025, ma_standard_sample_rate_8000 = 8000, ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ @@ -5390,7 +5390,7 @@ MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_ca /* Converts the given input data. -Both the input and output frames must be in the format specified in the config when the resampler was initilized. +Both the input and output frames must be in the format specified in the config when the resampler was initialized. On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use @@ -9133,8 +9133,6 @@ speakers or received from the microphone which can in turn result in de-syncs. Do not call this in any callback. -This will be called implicitly by `ma_device_uninit()`. - See Also -------- @@ -10171,7 +10169,7 @@ MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels typedef struct { - ma_data_source_vtable ds; + ma_data_source_base ds; ma_noise_config config; ma_lcg lcg; union @@ -10569,7 +10567,7 @@ typedef struct /* Extended processing callback. This callback is used for effects that process input and output at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two seperate frame counts: one for input, and one for output. + they take two separate frame counts: one for input, and one for output. On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. @@ -12238,7 +12236,7 @@ static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) #define ma_abs(x) (((x) > 0) ? (x) : -(x)) #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) ((x + (a-1)) & ~(a-1)) +#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) #define ma_align_64(x) ma_align(x, 8) #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) @@ -13639,7 +13637,7 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); if (length < 0) { - return MA_INVALID_OPERATION; /* An error occured when trying to convert the buffer. */ + return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ } if ((size_t)length < sizeof(pFormattedMessageStack)) { @@ -16180,7 +16178,15 @@ static void ma_thread_wait__posix(ma_thread* pThread) static ma_result ma_mutex_init__posix(ma_mutex* pMutex) { - int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); + int result; + + if (pMutex == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pMutex); + + result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); if (result != 0) { return ma_result_from_errno(result); } @@ -18452,7 +18458,7 @@ Timing *******************************************************************************/ #if defined(MA_WIN32) && !defined(MA_POSIX) static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - void ma_timer_init(ma_timer* pTimer) + static void ma_timer_init(ma_timer* pTimer) { LARGE_INTEGER counter; @@ -18464,7 +18470,7 @@ Timing pTimer->counter = counter.QuadPart; } - double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { LARGE_INTEGER counter; if (!QueryPerformanceCounter(&counter)) { @@ -18637,30 +18643,36 @@ static void ma_device__on_notification(ma_device_notification notification) } } -void ma_device__on_notification_started(ma_device* pDevice) +static void ma_device__on_notification_started(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); } -void ma_device__on_notification_stopped(ma_device* pDevice) +static void ma_device__on_notification_stopped(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); } -void ma_device__on_notification_rerouted(ma_device* pDevice) +/* Not all platforms support reroute notifications. */ +#if !defined(MA_EMSCRIPTEN) +static void ma_device__on_notification_rerouted(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); } +#endif -void ma_device__on_notification_interruption_began(ma_device* pDevice) +/* Interruptions are only used on some platforms. */ +#if defined(MA_APPLE_MOBILE) +static void ma_device__on_notification_interruption_began(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); } -void ma_device__on_notification_interruption_ended(ma_device* pDevice) +static void ma_device__on_notification_interruption_ended(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); } +#endif static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) @@ -19115,10 +19127,10 @@ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state n #if defined(MA_WIN32) - GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ #endif @@ -23270,7 +23282,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ } else { - /* An error occured and we need to abort. */ + /* An error occurred and we need to abort. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); result = ma_result_from_HRESULT(hr); break; @@ -34834,7 +34846,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "CoreFoundation.framework/CoreFoundation"); + pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MA_API_NOT_FOUND; } @@ -34843,7 +34855,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "CoreAudio.framework/CoreAudio"); + pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); if (pContext->coreaudio.hCoreAudio == NULL) { ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; @@ -34861,7 +34873,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to AudioToolbox. */ - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioUnit.framework/AudioUnit"); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); if (pContext->coreaudio.hAudioUnit == NULL) { ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); @@ -34871,7 +34883,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioToolbox.framework/AudioToolbox"); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); if (pContext->coreaudio.hAudioUnit == NULL) { ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); @@ -39956,7 +39968,7 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a /* With the audio worklet initialized we can now attach it to the graph. */ if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { - ma_result attachmentResult = EM_ASM_INT({ + ma_result attachmentResult = (ma_result)EM_ASM_INT({ var getUserMediaResult = 0; var audioWorklet = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); @@ -39987,7 +39999,7 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ if (pParameters->pConfig->deviceType == ma_device_type_playback) { - ma_result attachmentResult = EM_ASM_INT({ + ma_result attachmentResult = (ma_result)EM_ASM_INT({ var audioWorklet = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); audioWorklet.connect(audioContext.destination); @@ -40202,7 +40214,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co /* First thing we need is an AudioContext. */ var audioContextOptions = {}; - if (deviceType == window.miniaudio.device_type.playback) { + if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { audioContextOptions.sampleRate = sampleRate; } @@ -42100,10 +42112,23 @@ MA_API void ma_device_uninit(ma_device* pDevice) return; } - /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ - if (ma_device_is_started(pDevice)) { - ma_device_stop(pDevice); + /* + It's possible for the miniaudio side of the device and the backend to not be in sync due to + system-level situations such as the computer being put into sleep mode and the backend not + notifying miniaudio of the fact the device has stopped. It's possible for this to result in a + deadlock due to miniaudio thinking the device is in a running state, when in fact it's not + running at all. For this reason I am no longer explicitly stopping the device. I don't think + this should affect anyone in practice since uninitializing the backend will naturally stop the + device anyway. + */ + #if 0 + { + /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ + if (ma_device_is_started(pDevice)) { + ma_device_stop(pDevice); + } } + #endif /* Putting the device into an uninitialized state will make the worker thread return. */ ma_device__set_state(pDevice, ma_device_state_uninitialized); @@ -52835,7 +52860,7 @@ static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); } /* Tail. */ @@ -52861,7 +52886,7 @@ static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); } @@ -59701,7 +59726,7 @@ extern "C" { #define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) #define MA_DR_WAV_VERSION_MAJOR 0 #define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 12 +#define MA_DR_WAV_VERSION_REVISION 13 #define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) #include #define MA_DR_WAVE_FORMAT_PCM 0x1 @@ -64826,7 +64851,7 @@ MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_co /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); if (result != MA_SUCCESS) { - return MA_SUCCESS; + return result; } } @@ -64976,7 +65001,7 @@ MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decod /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); if (result != MA_SUCCESS) { - return MA_SUCCESS; + return result; } } @@ -68744,7 +68769,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; /* - Fences need to be acquired before doing anything. These must be aquired and released outside of + Fences need to be acquired before doing anything. These must be acquired and released outside of the node to ensure there's no holes where ma_fence_wait() could prematurely return before the data buffer has completed initialization. @@ -72016,7 +72041,7 @@ MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_n } if (heapLayout.outputBusOffset != MA_SIZE_MAX) { - pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); + pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); } else { pNodeBase->pOutputBuses = pNodeBase->_outputBuses; } @@ -72507,11 +72532,11 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde /* At this point we know that we are inside our start/stop times. However, we may need to adjust - our frame count and output pointer to accomodate since we could be straddling the time period + our frame count and output pointer to accommodate since we could be straddling the time period that this function is getting called for. It's possible (and likely) that the start time does not line up with the output buffer. We - therefore need to offset it by a number of frames to accomodate. The same thing applies for + therefore need to offset it by a number of frames to accommodate. The same thing applies for the stop time. */ timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; @@ -74097,7 +74122,7 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { fadeStartOffsetInFrames = 0; } else { - fadeStartOffsetInFrames -= ma_engine_get_time(pEngineNode->pEngine); + fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); } ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); @@ -75534,6 +75559,10 @@ MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 list *pOuterGain = 0; } + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } @@ -76103,7 +76132,7 @@ MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint } /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); return MA_SUCCESS; } @@ -76476,6 +76505,10 @@ MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadian *pOuterGain = 0; } + if (pSound == NULL) { + return; + } + ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } @@ -76757,6 +76790,8 @@ MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) { + ma_uint64 seekTarget; + if (pSound == NULL) { return MA_INVALID_ARGS; } @@ -76766,7 +76801,12 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* return MA_INVALID_OPERATION; } - return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); + seekTarget = ma_atomic_load_64(&pSound->seekTarget); + if (seekTarget != MA_SEEK_TARGET_NONE) { + *pCursor = seekTarget; + } else { + return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); + } } MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) @@ -76785,16 +76825,28 @@ MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) { - if (pSound == NULL) { - return MA_INVALID_ARGS; + ma_result result; + ma_uint64 cursorInPCMFrames; + ma_uint32 sampleRate; + + if (pCursor != NULL) { + *pCursor = 0; } - /* The notion of a cursor is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; + result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); + if (result != MA_SUCCESS) { + return result; } - return ma_data_source_get_cursor_in_seconds(pSound->pDataSource, pCursor); + result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; } MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) @@ -77193,8 +77245,8 @@ code below please report the bug to the respective repository for the relevant p #define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) #define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) #define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 -#define MA_DR_WAV_INT64_MIN ((ma_int64)0x80000000 << 32) -#define MA_DR_WAV_INT64_MAX ((((ma_int64)0x7FFFFFFF) << 32) | 0xFFFFFFFF) +#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) +#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) #if defined(_MSC_VER) && _MSC_VER >= 1400 #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC From 1cef62cf052432755e9955aa8798eca339dcf1d3 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 11:10:41 +0200 Subject: [PATCH 0107/1037] REVIEWED: `glfwGetError()` not availbale on `PLATFORM_WEB` fix #3470 --- src/platforms/rcore_web.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 3da23a054..e2373e45c 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -864,12 +864,12 @@ int InitPlatform(void) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); - result = glfwGetError(NULL); + result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web // Check context activation - if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) + if (result == true) //(result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation + CORE.Window.ready = true; int fbWidth = CORE.Window.screen.width; int fbHeight = CORE.Window.screen.height; From e4547eb4225189eadd2c6f4e87b5e32c4a285b88 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:56:03 +0200 Subject: [PATCH 0108/1037] Remove trail spaces --- HISTORY.md | 22 +++++++++++----------- src/rshapes.c | 6 +++--- src/rtext.c | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 735e66514..0a0b1ce2b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -379,13 +379,13 @@ Some numbers to start with: Highlights for `raylib 4.2`: - **raylib extra libraries cleanup**: raylib has been on diet and all the _extra_ libraries included on previous releases have been removed from raylib. Now raylib only includes the original **7** raylib modules: `rcore`, `rlgl`, `rshapes`, `rtextures`, `rtext`, `rmodels` and `raudio`. But no worries, _extra_ libraries have not been deleted, they have been moved to their own repos for better maintainability and more focus on its functionality. The libraries moved out from raylib repo are: [`raygui`](https://github.com/raysan5/raygui), [`physac`](https://github.com/raysan5/physac), [`rmem`](https://github.com/raylib-extras/rmem), [`reasings`](https://github.com/raylib-extras/reasings) and [`raudio`](https://github.com/raysan5/raudio) (standalone mode). On that same line, a new **amazing GitHub group:** [`raylib-extras`](https://github.com/raylib-extras) has been created by @JeffM2501 to contain raylib extra libraries as well as other raylib add-ons provided by the community. Jeff has done an amazing work on that line, providing multiple libraries and examples for raylib, like [custom first-person and third person camera systems](https://github.com/raylib-extras/extras-c/tree/main/cameras), [Dear ImGui raylib integration](https://github.com/raylib-extras/rlImGui), [multiple specific examples](https://github.com/raylib-extras/examples-c) and even a complete [RPG Game Example](https://github.com/raylib-extras/RPGExample)! Great work Jeff! :D - - - **raylib examples review**: The +120 raylib examples have been reviewed to add clearer information about when the were first created (raylib version used) and when they were updated for the last time. But the greatest improvement for users has been the **addition of an estimated difficulty level** for every example, [web has been updated accordingly](https://www.raylib.com/examples.html) to reflect those difficulty levels. Now examples are classified with **1 to 4 stars** depending on difficulty to help users with their learning process. Personally, I think this "small" addition could be a game-changer to better guide new users on the library adoption! Additionally, this new raylib release includes 7 new examples; the most interesting one: [`text_codepoints_loading`](https://www.raylib.com/examples/text/loader.html?name=text_codepoints_loading) that illustrates how to load and draw custom codepoints from a font file, very useful for Asian languages. + + - **raylib examples review**: The +120 raylib examples have been reviewed to add clearer information about when the were first created (raylib version used) and when they were updated for the last time. But the greatest improvement for users has been the **addition of an estimated difficulty level** for every example, [web has been updated accordingly](https://www.raylib.com/examples.html) to reflect those difficulty levels. Now examples are classified with **1 to 4 stars** depending on difficulty to help users with their learning process. Personally, I think this "small" addition could be a game-changer to better guide new users on the library adoption! Additionally, this new raylib release includes 7 new examples; the most interesting one: [`text_codepoints_loading`](https://www.raylib.com/examples/text/loader.html?name=text_codepoints_loading) that illustrates how to load and draw custom codepoints from a font file, very useful for Asian languages. - [**`rres 1.0`**](https://github.com/raysan5/rres): New `rres` **resources packaging file-format**, including a [`rres-raylib`](https://github.com/raysan5/rres/blob/master/src/rres-raylib.h) library implementation and [`rrespacker`](https://raylibtech.itch.io/rrespacker) tool. `rres` file format has been [under development for +8 years](https://github.com/raysan5/rres#design-history) and it was originally created to be part of raylib. It was highly inspired by _XNA XNB_ resources file format but design has changed a lot along the years. This first release of the format specs is engine-agnostic and has been designed to be portable to any engine, including lots of professional features like data processing, compression and encryption. - [**`raygui 3.2`**](https://github.com/raysan5/raygui): The **official raylib immediate-mode gui library** designed for tools development has been updated to a new version aligned with raylib 4.2. Multiple controls have been reviewed for library consistency, now all controls follow a similar function signature. It has been battle-tested with the development of +8 published tools in the last months. The tools can be seen and used for free in the [raylib technologies tools page](https://raylibtech.itch.io/). Worth mentioning that several of those **tools have been open sourced** for anyone to use, compile, contribute or learn how the code works. - + - [**`raylib_parser`**](https://github.com/raysan5/raylib/tree/master/parser): Multiple contributors **using the tool to automatize bindings creation** have contributed with improvements of this **tool to parse `raylib.h`** (and other raylib-style headers) to tokenize its enums, structs and functions. Processed data can be exported to custom file formats (i.e XML, JSON, LUA) for bindings generation or even docs generation if required. - **New file system API**: Current API has been redesigned to be more comprehensive and better aligned with raylib naming conventions, two new functions are provided `LoadDirectoryFiles()`/`LoadDirectoryFilesEx()` to load a `FilePathList` for provided path, supporting extension filtering and recursive directory scan. `LoadDroppedFiles()` has been renamed to better reflect its internal functionality. Now, all raylib functions that start with `Load*()` allocate memory internally and a equivalent `Unload*()` function is defined to take care of that memory internally when not required any more! @@ -414,21 +414,21 @@ Highlights for `raylib 4.5`: - **`NEW` Improved ANGLE support on Desktop platforms**: Support for OpenGL ES 2.0 on Desktop platforms (Windows, Linux, macOS) has been reviewed by @wtnbgo GitHub user. Now raylib can be compiled on desktop for OpenGL ES 2.0 and linked against [`ANGLE`](https://github.com/google/angle). This _small_ addition open the door to building raylib for all **ANGLE supported backends: Direct3D 11, Vulkan and Metal**. Please note that this new feature is still experimental and requires further testing! - **`NEW` Camera module**: A brand new implementation from scratch for `rcamera` module, contributed by @Crydsch GitHub user! **New camera system is simpler, more flexible, more granular and more extendable**. Specific camera math transformations (movement/rotation) have been moved to individual functions, exposing them to users if required. Global state has been removed from the module and standalone usage has been greatly improved; now `rcamera.h` single-file header-only library can be used externally, independently of raylib. A new `UpdateCameraPro()` function has been added to address input-dependency of `UpdateCamera()`, now advance users have **full control over camera inputs and movement/rotation speeds**! - + - **`NEW` Support for M3D models and M3D/GLTF animations**: 3d models animations support has been a limited aspect of raylib for long time, some versions ago IQM animations were supported but raylib 4.5 also adds support for the brand new [M3D file format](https://bztsrc.gitlab.io/model3d/), including animations and the long expected support for **GLTF animations**! The new M3D file format is **simple, portable, feature complete, extensible and open source**. It also provides a complete set of tools to export/visualize M3D models from/to Blender! Now raylib supports up to **3 model file-formats with animations**: `IQM`, `GLTF` and `M3D`. - + - **`NEW` Support QOA audio format (import/export)**: Just a couple of months ago the new [QOA file format](https://qoaformat.org/) was published, a very simple, portable and open source quite-ok-audio file format. raylib already supports it, added to `raudio` module and including audio loading from file, loading from memory, streaming from file, streaming from memory and **exporting to QOA** audio format. **Because simplicity really matters to raylib!** - + - **`NEW` Module for compressed textures loading**: [`rl_gputex`](https://github.com/raysan5/raylib/blob/master/src/external/rl_gputex.h), a portable single-file header-only small library to load compressed texture file-formats (DDS, PKM, KTX, PVR, ASTC). Provided functionality is not new to raylib but it was part of the raylib `rtextures` module, now it has been moved into a separate self-contained library, **improving portability**. Note that this module is only intended to **load compressed data from files, ready to be uploaded to GPU**, no compression/decompression functionality is provided. This change is a first step towards a better modularization of raylib library. - + - **Reviewed `rlgl` module for automatic limits checking**: Again, [`rlgl`](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) has been reviewed to simplify usage. Now users do not need to worry about reaching the internal render-batch limits when they send their triangles to draw 2d/3d, `rlgl` manages it automatically! This change allows a **great simplification for other modules** like `rshapes`, `rtextures` and `rmodels` that do not need to worry about bufffer overflows and can just define as many vertex as desired! - - - **Reviewed `rshapes` module to minimize the rlgl dependency**: Now `rshapes` 2d shapes drawing functions **only depend on 6 low-level functions**: `rlBegin()`, `rlEnd()`, `rlVertex3f()`, `rlTexCoord2f()`, `rlNormal3f()`, `rlSetTexture()`. With only those pseudo-OpenGl 1.1 minimal functionality, everything can be drawn! This improvement converts `rshapes` module in a **self-contained, portable shapes-drawing library that can be used independently of raylib**, as far as entry points for those 6 functions are provided by the user. It even allows to be used for software rendering, with the proper backend! + + - **Reviewed `rshapes` module to minimize the rlgl dependency**: Now `rshapes` 2d shapes drawing functions **only depend on 6 low-level functions**: `rlBegin()`, `rlEnd()`, `rlVertex3f()`, `rlTexCoord2f()`, `rlNormal3f()`, `rlSetTexture()`. With only those pseudo-OpenGl 1.1 minimal functionality, everything can be drawn! This improvement converts `rshapes` module in a **self-contained, portable shapes-drawing library that can be used independently of raylib**, as far as entry points for those 6 functions are provided by the user. It even allows to be used for software rendering, with the proper backend! - **Added data structures validation functions**: Multiple functions have been added by @RobLoach GitHub user to ease the validation of raylib data structures: `IsImageReady()`, `IsTextureReady()`, `IsSoundReady()`... Now users have a simple mechanism to **make sure data has been correctly loaded**, instead of checking internal structure values by themselfs. - + As usual, those are only some highlights but there is much more! New image generators, new color transformation functionality, improved blending support for color/alpha, etc... Make sure to check raylib [CHANGELOG]([CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG)) for a detailed list of changes! Please, note that all breaking changes have been flagged with a `WARNING` in the CHANGELOG, specially useful for binding creators! -**raylib keeps improving one more version** with a special focus on maintainability and sustainability. Always working towards making the library more **simple and easy-to-use**. +**raylib keeps improving one more version** with a special focus on maintainability and sustainability. Always working towards making the library more **simple and easy-to-use**. Let's keep **enjoying games/tools/graphics programming!** :) diff --git a/src/rshapes.c b/src/rshapes.c index de64f1593..e8e533d30 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -190,7 +190,7 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) if ((length > 0) && (thick > 0)) { float scale = thick/(2*length); - + Vector2 radius = { -scale*delta.y, scale*delta.x }; Vector2 strip[4] = { { startPos.x - radius.x, startPos.y - radius.y }, @@ -255,7 +255,7 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - + float a = powf(1.0f - t, 2); float b = 2.0f*(1.0f - t)*t; float c = powf(t, 2); @@ -301,7 +301,7 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - + float a = powf(1.0f - t, 3); float b = 3.0f*powf(1.0f - t, 2)*t; float c = 3.0f*(1.0f - t)*powf(t, 2); diff --git a/src/rtext.c b/src/rtext.c index 5b43bfb92..b83eb171b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1349,7 +1349,7 @@ unsigned int TextLength(const char *text) if (text != NULL) { // NOTE: Alternative: use strlen(text) - + while (*text++) length++; } @@ -1418,7 +1418,7 @@ int TextCopy(char *dst, const char *src) if ((src != NULL) && (dst != NULL)) { // NOTE: Alternative: use strcpy(dst, src) - + while (*src != '\0') { *dst = *src; @@ -1463,7 +1463,7 @@ const char *TextSubtext(const char *text, int position, int length) } if (length >= textLength) length = textLength; - + // NOTE: Alternative: memcpy(buffer, text + position, length) for (int c = 0 ; c < length ; c++) From d0141bd105b491fbef9ea5fb8c3ba26ba0432717 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:56:38 +0200 Subject: [PATCH 0109/1037] Remove trail spaces --- src/platforms/rcore_android.c | 4 ++-- src/platforms/rcore_drm.c | 14 +++++++------- src/platforms/rcore_template.c | 14 +++++++------- src/platforms/rcore_web.c | 12 ++++++------ 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 4faf73b7d..83450bb0a 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -547,7 +547,7 @@ int InitPlatform(void) //AConfiguration_getKeyboard(platform.app->config); //AConfiguration_getScreenSize(platform.app->config); //AConfiguration_getScreenLong(platform.app->config); - + // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false @@ -569,7 +569,7 @@ int InitPlatform(void) // Initialize storage system //---------------------------------------------------------------------------- InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize assets manager - + CORE.Storage.basePath = platform.app->activity->internalDataPath; // Define base path for storage //---------------------------------------------------------------------------- diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 9609c50d2..6f459b1af 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -864,14 +864,14 @@ int InitPlatform(void) // There must be at least one frame displayed before the buffers are swapped //eglSwapInterval(platform.device, 1); - + EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); // Check surface and context activation if (result != EGL_FALSE) { CORE.Window.ready = true; - + CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; @@ -883,9 +883,9 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } @@ -905,7 +905,7 @@ int InitPlatform(void) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - + // Initialize input events system //---------------------------------------------------------------------------- InitEvdevInput(); // Evdev inputs initialization @@ -922,7 +922,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); return 0; diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 11ce45c1e..1cebfa798 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -522,7 +522,7 @@ int InitPlatform(void) if (result != EGL_FALSE) { CORE.Window.ready = true; - + CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; @@ -534,13 +534,13 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } //---------------------------------------------------------------------------- - + // If everything work as expected, we can continue CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; @@ -558,7 +558,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - + // TODO: Initialize input events system // It could imply keyboard, mouse, gamepad, touch... // Depending on the platform libraries/SDK it could use a callbacks mechanims @@ -576,7 +576,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully"); return 0; diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index e2373e45c..d797d99d7 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -865,7 +865,7 @@ int InitPlatform(void) glfwMakeContextCurrent(platform.handle); result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web - + // Check context activation if (result == true) //(result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { @@ -885,9 +885,9 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } @@ -896,12 +896,12 @@ int InitPlatform(void) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(glfwGetProcAddress); //---------------------------------------------------------------------------- - + // Initialize input events callbacks //---------------------------------------------------------------------------- // Setup callback functions for the DOM events From 067dbe8657436e4778a91ea69c260f5beba48ec6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:57:07 +0200 Subject: [PATCH 0110/1037] ADDED: Drop files support to `PLATFORM_DESKTOP_SDL` --- src/platforms/rcore_desktop_sdl.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index ccc2acf0a..8543c751c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -998,6 +998,33 @@ void PollInputEvents(void) switch (event.type) { case SDL_QUIT: CORE.Window.shouldClose = true; break; + + case SDL_DROPFILE: // Dropped file + { + if (CORE.Window.dropFileCount == 0) + { + // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files + // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed + // TODO: Pointers should probably be reallocated for any new file added... + CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *)); + + CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); + SDL_free(event.drop.file); + + CORE.Window.dropFileCount++; + } + else if (CORE.Window.dropFileCount < 1024) + { + CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); + SDL_free(event.drop.file); + + CORE.Window.dropFileCount++; + } + else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!"); + + } break; // Window events are also polled (Minimized, maximized, close...) case SDL_WINDOWEVENT: @@ -1247,6 +1274,8 @@ int InitPlatform(void) SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } + + SDL_EventState(SDL_DROPFILE, SDL_ENABLE); //---------------------------------------------------------------------------- // Initialize timming system From 99dac5451cad1c45c0a6b1da5c6175a7575f3403 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:59:19 +0200 Subject: [PATCH 0111/1037] ADDED: Automation Events System, exposed to users Added new API to record and play events Added examples illustrating functionality --- examples/core/core_automation_events.c | 289 +++++++ .../examples/core_automation_events.vcxproj | 390 +++++++++ projects/VS2022/raylib.sln | 19 + src/config.h | 3 +- src/raylib.h | 23 + src/rcore.c | 761 ++++++++++-------- src/rcore.h | 4 + 7 files changed, 1155 insertions(+), 334 deletions(-) create mode 100644 examples/core/core_automation_events.c create mode 100644 projects/VS2022/examples/core_automation_events.vcxproj diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c new file mode 100644 index 000000000..d011d6304 --- /dev/null +++ b/examples/core/core_automation_events.c @@ -0,0 +1,289 @@ +/******************************************************************************************* +* +* raylib [core] example - automation events +* +* Example originally created with raylib 5.0, last time updated with raylib 5.0 +* +* Example based on 2d_camera_platformer example by arvyy (@arvyy) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2023 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +#define GRAVITY 400 +#define PLAYER_JUMP_SPD 350.0f +#define PLAYER_HOR_SPD 200.0f + +#define MAX_ENVIRONMENT_ELEMENTS 5 + +typedef struct Player { + Vector2 position; + float speed; + bool canJump; +} Player; + +typedef struct EnvElement { + Rectangle rect; + int blocking; + Color color; +} EnvElement; + + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - automation events"); + + // Define player + Player player = { 0 }; + player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + // Define environment elements (platforms) + EnvElement envElements[MAX_ENVIRONMENT_ELEMENTS] = { + {{ 0, 0, 1000, 400 }, 0, LIGHTGRAY }, + {{ 0, 400, 1000, 200 }, 1, GRAY }, + {{ 300, 200, 400, 10 }, 1, GRAY }, + {{ 250, 300, 100, 10 }, 1, GRAY }, + {{ 650, 300, 100, 10 }, 1, GRAY } + }; + + // Define camera + Camera2D camera = { 0 }; + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; + + // Automation events + AutomationEventList aelist = LoadAutomationEventList(0); // Initialize list of automation events to record new events + SetAutomationEventList(&aelist); + bool eventRecording = false; + bool eventPlaying = false; + + int frameCounter = 0; + int playFrameCounter = 0; + int currentFrame = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + float deltaTime = GetFrameTime(); + + // Dropped files logic + //---------------------------------------------------------------------------------- + if (IsFileDropped()) + { + FilePathList droppedFiles = LoadDroppedFiles(); + + // Supports loading .rgs style files (text or binary) and .png style palette images + if (IsFileExtension(droppedFiles.paths[0], ".txt;.rae")) + { + UnloadAutomationEventList(&aelist); + aelist = LoadAutomationEventList(droppedFiles.paths[0]); + + eventRecording = false; + + // Reset scene state to play + eventPlaying = true; + playFrameCounter = 0; + + player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; + } + + UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory + } + //---------------------------------------------------------------------------------- + + // Update player + //---------------------------------------------------------------------------------- + if (IsKeyDown(KEY_LEFT)) player.position.x -= PLAYER_HOR_SPD*deltaTime; + if (IsKeyDown(KEY_RIGHT)) player.position.x += PLAYER_HOR_SPD*deltaTime; + if (IsKeyDown(KEY_SPACE) && player.canJump) + { + player.speed = -PLAYER_JUMP_SPD; + player.canJump = false; + } + + int hitObstacle = 0; + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) + { + EnvElement *element = &envElements[i]; + Vector2 *p = &(player.position); + if (element->blocking && + element->rect.x <= p->x && + element->rect.x + element->rect.width >= p->x && + element->rect.y >= p->y && + element->rect.y <= p->y + player.speed*deltaTime) + { + hitObstacle = 1; + player.speed = 0.0f; + p->y = element->rect.y; + } + } + + if (!hitObstacle) + { + player.position.y += player.speed*deltaTime; + player.speed += GRAVITY*deltaTime; + player.canJump = false; + } + else player.canJump = true; + + camera.zoom += ((float)GetMouseWheelMove()*0.05f); + + if (camera.zoom > 3.0f) camera.zoom = 3.0f; + else if (camera.zoom < 0.25f) camera.zoom = 0.25f; + + if (IsKeyPressed(KEY_R)) + { + camera.zoom = 1.0f; + player.position = (Vector2){ 400, 280 }; + } + //---------------------------------------------------------------------------------- + + // Update camera + //---------------------------------------------------------------------------------- + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000; + + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) + { + EnvElement *element = &envElements[i]; + minX = fminf(element->rect.x, minX); + maxX = fmaxf(element->rect.x + element->rect.width, maxX); + minY = fminf(element->rect.y, minY); + maxY = fmaxf(element->rect.y + element->rect.height, maxY); + } + + Vector2 max = GetWorldToScreen2D((Vector2){ maxX, maxY }, camera); + Vector2 min = GetWorldToScreen2D((Vector2){ minX, minY }, camera); + + if (max.x < screenWidth) camera.offset.x = screenWidth - (max.x - screenWidth/2); + if (max.y < screenHeight) camera.offset.y = screenHeight - (max.y - screenHeight/2); + if (min.x > 0) camera.offset.x = screenWidth/2 - min.x; + if (min.y > 0) camera.offset.y = screenHeight/2 - min.y; + //---------------------------------------------------------------------------------- + + // Toggle events recording + if (IsKeyPressed(KEY_ONE)) + { + if (!eventPlaying) + { + if (eventRecording) + { + StopAutomationEventRecording(); + eventRecording = false; + + ExportAutomationEventList(aelist, "automation.rae"); + } + else + { + StartAutomationEventRecording(); + eventRecording = true; + } + } + } + + if (eventPlaying) + { + if (playFrameCounter == aelist.events[currentFrame].frame) + { + PlayAutomationEvent(aelist.events[currentFrame]); + currentFrame++; + + if (currentFrame == aelist.count) + { + eventPlaying = false; + currentFrame = 0; + playFrameCounter = 0; + } + } + + playFrameCounter++; + } + + if (eventRecording || eventPlaying) frameCounter++; + else frameCounter = 0; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(LIGHTGRAY); + + BeginMode2D(camera); + + // Draw environment elements + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) + { + DrawRectangleRec(envElements[i].rect, envElements[i].color); + } + + // Draw player rectangle + DrawRectangleRec((Rectangle){ player.position.x - 20, player.position.y - 40, 40, 40 }, RED); + + EndMode2D(); + + // Draw automation events recording indicator + if (eventRecording) + { + if (((frameCounter/15)%2) == 1) + { + DrawCircle(GetScreenWidth() - 200, 20, 10, MAROON); + DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), GetScreenWidth() - 180, 15, 10, RED); + } + } + else if (eventPlaying) + { + if (((frameCounter/15)%2) == 1) + { + DrawTriangle((Vector2){ GetScreenWidth() - 200, 10 }, (Vector2){ GetScreenWidth() - 200, 30 }, (Vector2){ GetScreenWidth() - 200 + 20, 20 }, DARKGREEN); + DrawText(TextFormat("PLAYING EVENTS... [%i]", currentFrame), GetScreenWidth() - 170, 15, 10, LIME); + } + } + + DrawText("Controls:", 20, 20, 10, BLACK); + DrawText("- Right/Left to move", 30, 40, 10, DARKGRAY); + DrawText("- Space to jump", 30, 60, 10, DARKGRAY); + DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 30, 80, 10, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/projects/VS2022/examples/core_automation_events.vcxproj b/projects/VS2022/examples/core_automation_events.vcxproj new file mode 100644 index 000000000..e70a6b1ea --- /dev/null +++ b/projects/VS2022/examples/core_automation_events.vcxproj @@ -0,0 +1,390 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26} + Win32Proj + core_automation_events + 10.0 + core_automation_events + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 007f796f9..167c60f3f 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -273,6 +273,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "exampl EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_split_screen", "examples\core_2d_camera_split_screen.vcxproj", "{CC62F7DB-D089-4677-8575-CAB7A7815C43}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_automation_events", "examples\core_automation_events.vcxproj", "{7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -2297,6 +2299,22 @@ Global {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.Build.0 = Release|x64 {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.ActiveCfg = Release|Win32 {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.Build.0 = Release|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.ActiveCfg = Debug|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.Build.0 = Debug|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.ActiveCfg = Debug|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.Build.0 = Debug|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.ActiveCfg = Release|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.Build.0 = Release|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.ActiveCfg = Release|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2435,6 +2453,7 @@ Global {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {F81C5819-85B4-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {CC62F7DB-D089-4677-8575-CAB7A7815C43} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/src/config.h b/src/config.h index 7886aeeba..96fdd065f 100644 --- a/src/config.h +++ b/src/config.h @@ -63,7 +63,7 @@ // Support CompressData() and DecompressData() functions #define SUPPORT_COMPRESSION_API 1 // Support automatic generated events, loading and recording of those events when required -//#define SUPPORT_EVENTS_AUTOMATION 1 +#define SUPPORT_AUTOMATION_EVENTS 1 // Support custom frame control, only for advance users // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() // Enabling this flag allows manual control of the frame processes, use at your own risk @@ -85,6 +85,7 @@ #define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB +#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record //------------------------------------------------------------------------------------ // Module: rlgl - Configuration values diff --git a/src/raylib.h b/src/raylib.h index b172562d0..7b3eddaee 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -506,6 +506,20 @@ typedef struct FilePathList { char **paths; // Filepaths entries } FilePathList; +// Automation event (opaque struct) +typedef struct AutomationEvent { + unsigned int frame; // Event frame + unsigned int type; // Event type (AutomationEventType) + int params[4]; // Event parameters (if required) +} AutomationEvent; + +// Automation event list +typedef struct AutomationEventList { + unsigned int capacity; // Events max entries (MAX_AUTOMATION_EVENTS) + unsigned int count; // Events entries count + AutomationEvent *events; // Events entries +} AutomationEventList; + //---------------------------------------------------------------------------------- // Enumerators Definition //---------------------------------------------------------------------------------- @@ -1114,6 +1128,15 @@ RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataS RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() +// Automation events functionality +RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS +RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file +RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file +RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to +RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set) +RLAPI void StopAutomationEventRecording(void); // Stop recording automation events +RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event + //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ diff --git a/src/rcore.c b/src/rcore.c index ef3beaee3..67c79efa9 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,17 +3,17 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP: +* - PLATFORM_DESKTOP: * > Windows (Win32, Win64) * > Linux (X11/Wayland desktop mode) * > macOS/OSX (x64, arm64) * > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - PLATFORM_WEB: +* - PLATFORM_WEB: * > HTML5 (WebAssembly) -* - PLATFORM_DRM: +* - PLATFORM_DRM: * > Raspberry Pi 0-5 * > Linux native mode (KMS driver) -* - PLATFORM_ANDROID: +* - PLATFORM_ANDROID: * > Android (ARM, ARM64) * * CONFIGURATION: @@ -48,8 +48,8 @@ * provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module * for linkage * -* #define SUPPORT_EVENTS_AUTOMATION -* Support automatic generated events, loading and recording of those events when required +* #define SUPPORT_AUTOMATION_EVENTS +* Support automatic events recording and playing, useful for automated testing systems or AI based game playing * * DEPENDENCIES: * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) @@ -183,9 +183,8 @@ bool gifRecording = false; // GIF recording state MsfGifState gifState = { 0 }; // MSGIF context state #endif -#if defined(SUPPORT_EVENTS_AUTOMATION) -#define MAX_CODE_AUTOMATION_EVENTS 16384 - +#if defined(SUPPORT_AUTOMATION_EVENTS) +// Automation events type typedef enum AutomationEventType { EVENT_NONE = 0, // Input events @@ -212,12 +211,12 @@ typedef enum AutomationEventType { WINDOW_MINIMIZE, // no params WINDOW_RESIZE, // param[0]: width, param[1]: height // Custom events - ACTION_TAKE_SCREENSHOT, - ACTION_SETTARGETFPS + ACTION_TAKE_SCREENSHOT, // no params + ACTION_SETTARGETFPS // param[0]: fps } AutomationEventType; -// Event type -// Used to enable events flags +// Event type to config events flags +// TODO: Not used at the moment typedef enum { EVENT_INPUT_KEYBOARD = 0, EVENT_INPUT_MOUSE = 1, @@ -228,6 +227,7 @@ typedef enum { EVENT_CUSTOM = 32 } EventType; +// Event type name strings, required for export static const char *autoEventTypeName[] = { "EVENT_NONE", "INPUT_KEY_UP", @@ -255,19 +255,19 @@ static const char *autoEventTypeName[] = { "ACTION_SETTARGETFPS" }; +/* // Automation event (24 bytes) -typedef struct AutomationEvent { +// NOTE: Opaque struct, internal to raylib +struct AutomationEvent { unsigned int frame; // Event frame unsigned int type; // Event type (AutomationEventType) int params[4]; // Event parameters (if required) -} AutomationEvent; +}; +*/ -static AutomationEvent *events = NULL; // Events array -static unsigned int eventCount = 0; // Events count -static bool eventsPlaying = false; // Play events -static bool eventsRecording = false; // Record events - -//static short eventsEnabled = 0b0000001111111111; // Events enabled for checking +static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer +static bool automationEventRecording = false; // Recording automation events flag +//static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing #endif //----------------------------------------------------------------------------------- @@ -291,11 +291,8 @@ static void SetupViewport(int width, int height); // Set viewport for static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories recursively from a base path -#if defined(SUPPORT_EVENTS_AUTOMATION) -static void LoadAutomationEvents(const char *fileName); // Load automation events from file -static void ExportAutomationEvents(const char *fileName); // Export recorded automation events into a file -static void RecordAutomationEvent(unsigned int frame); // Record frame events (to internal events array) -static void PlayAutomationEvent(unsigned int frame); // Play frame events (from internal events array) +#if defined(SUPPORT_AUTOMATION_EVENTS) +static void RecordAutomationEvent(void); // Record frame events (to internal events array) #endif #if defined(_WIN32) @@ -304,14 +301,14 @@ void __stdcall Sleep(unsigned long msTimeout); // Required for: Wai #endif #if !defined(SUPPORT_MODULE_RTEXT) -const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' +const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' #endif // !SUPPORT_MODULE_RTEXT // Include platform-specific submodules #if defined(PLATFORM_DESKTOP) #include "platforms/rcore_desktop.c" #elif defined(PLATFORM_DESKTOP_SDL) - #include "platforms/rcore_desktop_sdl.c" + #include "platforms/rcore_desktop_sdl.c" #elif defined(PLATFORM_WEB) #include "platforms/rcore_web.c" #elif defined(PLATFORM_DRM) @@ -434,12 +431,12 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - + // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- - + // Initialize rlgl default data (buffers and shaders) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -485,9 +482,6 @@ void InitWindow(int width, int height, const char *title) #endif CORE.Time.frameCounter = 0; -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); -#endif // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); @@ -512,14 +506,10 @@ void CloseWindow(void) rlglClose(); // De-init rlgl // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - CORE.Window.ready = false; TRACELOG(LOG_INFO, "Window closed successfully"); } @@ -684,34 +674,6 @@ void EndDrawing(void) } #endif -#if defined(SUPPORT_EVENTS_AUTOMATION) - // Draw record/play indicator - if (eventsRecording) - { - gifFrameCounter++; - - if (((gifFrameCounter/15)%2) == 1) - { - DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON); - DrawText("EVENTS RECORDING", 50, CORE.Window.screen.height - 25, 10, RED); - } - - rlDrawRenderBatchActive(); // Update and draw internal render batch - } - else if (eventsPlaying) - { - gifFrameCounter++; - - if (((gifFrameCounter/15)%2) == 1) - { - DrawCircle(30, CORE.Window.screen.height - 20, 10, LIME); - DrawText("EVENTS PLAYING", 50, CORE.Window.screen.height - 25, 10, GREEN); - } - - rlDrawRenderBatchActive(); // Update and draw internal render batch - } -#endif - #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL) SwapScreenBuffer(); // Copy back buffer to front buffer (screen) @@ -775,16 +737,9 @@ void EndDrawing(void) } #endif // SUPPORT_SCREEN_CAPTURE -#if defined(SUPPORT_EVENTS_AUTOMATION) - // Events recording and playing logic - if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter); - else if (eventsPlaying) - { - // TODO: When should we play? After/before/replace PollInputEvents()? - if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false; - PlayAutomationEvent(CORE.Time.frameCounter); - } -#endif // SUPPORT_EVENTS_AUTOMATION +#if defined(SUPPORT_AUTOMATION_EVENTS) + if (automationEventRecording) RecordAutomationEvent(); // Event recording +#endif CORE.Time.frameCounter++; } @@ -1470,7 +1425,7 @@ float GetFrameTime(void) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//void SwapScreenBuffer(void); +//void SwapScreenBuffer(void); //void PollInputEvents(void); // Wait for some time (stop program execution) @@ -1481,7 +1436,7 @@ float GetFrameTime(void) void WaitTime(double seconds) { if (seconds < 0) return; - + #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) double destinationTime = GetTime() + seconds; #endif @@ -2180,6 +2135,237 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) return decodedData; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Automation Events Recording and Playing +//---------------------------------------------------------------------------------- + +// Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS +AutomationEventList LoadAutomationEventList(const char *fileName) +{ + AutomationEventList list = { 0 }; + + // Allocate and empty automation event list, ready to record new events + list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + list.capacity = MAX_AUTOMATION_EVENTS; + +#if defined(SUPPORT_AUTOMATION_EVENTS) + if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully"); + else + { + // Load automation events file (binary) + /* + //int dataSize = 0; + //unsigned char *data = LoadFileData(fileName, &dataSize); + + FILE *raeFile = fopen(fileName, "rb"); + unsigned char fileId[4] = { 0 }; + + fread(fileId, 1, 4, raeFile); + + if ((fileId[0] == 'r') && (fileId[1] == 'A') && (fileId[2] == 'E') && (fileId[1] == ' ')) + { + fread(&eventCount, sizeof(int), 1, raeFile); + TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount); + fread(events, sizeof(AutomationEvent), eventCount, raeFile); + } + + fclose(raeFile); + */ + + // Load events file (text) + //unsigned char *buffer = LoadFileText(fileName); + FILE *raeFile = fopen(fileName, "rt"); + + if (raeFile != NULL) + { + unsigned int counter = 0; + char buffer[256] = { 0 }; + char eventDesc[64] = { 0 }; + + fgets(buffer, 256, raeFile); + + while (!feof(raeFile)) + { + switch (buffer[0]) + { + case 'c': sscanf(buffer, "c %i", &list.count); break; + case 'e': + { + sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type, + &list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc); + + counter++; + } break; + default: break; + } + + fgets(buffer, 256, raeFile); + } + + if (counter != list.count) + { + TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count); + list.count = counter; + } + + fclose(raeFile); + + TRACELOG(LOG_INFO, "AUTOMATION: Events file loaded successfully"); + } + + TRACELOG(LOG_INFO, "AUTOMATION: Events loaded from file: %i", list.count); + } +#endif + return list; +} + +// Unload automation events list from file +void UnloadAutomationEventList(AutomationEventList *list) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + RL_FREE(list->events); + list->events = NULL; + list->count = 0; + list->capacity = 0; +#endif +} + +// Export automation events list as text file +bool ExportAutomationEventList(AutomationEventList list, const char *fileName) +{ + bool success = false; + +#if defined(SUPPORT_AUTOMATION_EVENTS) + // Export events as binary file + // TODO: Save to memory buffer and SaveFileData() + /* + unsigned char fileId[4] = "rAE "; + FILE *raeFile = fopen(fileName, "wb"); + fwrite(fileId, sizeof(unsigned char), 4, raeFile); + fwrite(&eventCount, sizeof(int), 1, raeFile); + fwrite(events, sizeof(AutomationEvent), eventCount, raeFile); + fclose(raeFile); + */ + + // Export events as text + // TODO: Save to memory buffer and SaveFileText() + char *txtData = (char *)RL_CALLOC(256*list.count + 2048, sizeof(char)); // 256 characters per line plus some header + + int byteCount = 0; + byteCount += sprintf(txtData + byteCount, "#\n"); + byteCount += sprintf(txtData + byteCount, "# Automation events exporter v1.0 - raylib automation events list\n"); + byteCount += sprintf(txtData + byteCount, "#\n"); + byteCount += sprintf(txtData + byteCount, "# c \n"); + byteCount += sprintf(txtData + byteCount, "# e // \n"); + byteCount += sprintf(txtData + byteCount, "#\n"); + byteCount += sprintf(txtData + byteCount, "# more info and bugs-report: github.com/raysan5/raylib\n"); + byteCount += sprintf(txtData + byteCount, "# feedback and support: ray[at]raylib.com\n"); + byteCount += sprintf(txtData + byteCount, "#\n"); + byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2024 Ramon Santamaria (@raysan5)\n"); + byteCount += sprintf(txtData + byteCount, "#\n\n"); + + // Add events data + byteCount += sprintf(txtData + byteCount, "c %i\n", list.count); + for (int i = 0; i < list.count; i++) + { + byteCount += sprintf(txtData + byteCount, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type, + list.events[i].params[0], list.events[i].params[1], list.events[i].params[2], list.events[i].params[3], autoEventTypeName[list.events[i].type]); + } + + // NOTE: Text data size exported is determined by '\0' (NULL) character + success = SaveFileText(fileName, txtData); + + RL_FREE(txtData); +#endif + + return success; +} + +// Setup automation event list to record to +void SetAutomationEventList(AutomationEventList *list) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + currentEventList = list; +#endif +} + +// Start recording automation events (AutomationEventList must be set) +void StartAutomationEventRecording(void) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + automationEventRecording = true; +#endif +} + +// Stop recording automation events +void StopAutomationEventRecording(void) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + automationEventRecording = false; +#endif +} + +// Play a recorded automation event +void PlayAutomationEvent(AutomationEvent event) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + // WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user! + + if (!automationEventRecording) // TODO: Allow recording events while playing? + { + switch (event.type) + { + // Input event + case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break; // param[0]: key + case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[event.params[0]] = true; break; // param[0]: key + case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break; // param[0]: key + case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break; // param[0]: key + case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y + { + CORE.Input.Mouse.currentPosition.x = (float)event.params[0]; + CORE.Input.Mouse.currentPosition.y = (float)event.params[1]; + } break; + case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta + { + CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0]; break; + CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1]; break; + } break; + case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[event.params[0]] = false; break; // param[0]: id + case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[event.params[0]] = true; break; // param[0]: id + case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y + { + CORE.Input.Touch.position[event.params[0]].x = (float)event.params[1]; + CORE.Input.Touch.position[event.params[0]].y = (float)event.params[2]; + } break; + case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[event.params[0]] = true; break; // param[0]: gamepad + case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[event.params[0]] = false; break; // param[0]: gamepad + case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = false; break; // param[0]: gamepad, param[1]: button + case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = true; break; // param[0]: gamepad, param[1]: button + case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta + { + CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f); + } break; + case INPUT_GESTURE: GESTURES.current = event.params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current + + // Window event + case WINDOW_CLOSE: CORE.Window.shouldClose = true; break; + case WINDOW_MAXIMIZE: MaximizeWindow(); break; + case WINDOW_MINIMIZE: MinimizeWindow(); break; + case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break; + + // Custom event + case ACTION_TAKE_SCREENSHOT: + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } break; + case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break; + default: break; + } + } +#endif +} + //---------------------------------------------------------------------------------- // Module Functions Definition: Input Handling: Keyboard //---------------------------------------------------------------------------------- @@ -2793,233 +2979,180 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath); } -#if defined(SUPPORT_EVENTS_AUTOMATION) -// NOTE: Loading happens over AutomationEvent *events -// TODO: This system should probably be redesigned -static void LoadAutomationEvents(const char *fileName) +#if defined(SUPPORT_AUTOMATION_EVENTS) +// Automation event recording +// NOTE: Recording is by default done at EndDrawing(), after PollInputEvents() +static void RecordAutomationEvent(void) { - // Load events file (binary) - /* - FILE *repFile = fopen(fileName, "rb"); - unsigned char fileId[4] = { 0 }; + // Checking events in current frame and save them into currentEventList + // TODO: How important is the current frame? Could it be modified? + + if (currentEventList->count == currentEventList->capacity) return; // Security check - fread(fileId, 1, 4, repFile); - - if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) - { - fread(&eventCount, sizeof(int), 1, repFile); - TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount); - fread(events, sizeof(AutomationEvent), eventCount, repFile); - } - - fclose(repFile); - */ - - // Load events file (text) - FILE *repFile = fopen(fileName, "rt"); - - if (repFile != NULL) - { - unsigned int count = 0; - char buffer[256] = { 0 }; - - fgets(buffer, 256, repFile); - - while (!feof(repFile)) - { - if (buffer[0] == 'c') sscanf(buffer, "c %i", &eventCount); - else if (buffer[0] == 'e') - { - sscanf(buffer, "e %d %d %d %d %d", &events[count].frame, &events[count].type, - &events[count].params[0], &events[count].params[1], &events[count].params[2]); - - count++; - } - - fgets(buffer, 256, repFile); - } - - if (count != eventCount) TRACELOG(LOG_WARNING, "Events count provided is different than count"); - - fclose(repFile); - } - - TRACELOG(LOG_WARNING, "Events loaded: %i", eventCount); -} - -// Export recorded events into a file -static void ExportAutomationEvents(const char *fileName) -{ - unsigned char fileId[4] = "rEP "; - - // Save as binary - /* - FILE *repFile = fopen(fileName, "wb"); - fwrite(fileId, sizeof(unsigned char), 4, repFile); - fwrite(&eventCount, sizeof(int), 1, repFile); - fwrite(events, sizeof(AutomationEvent), eventCount, repFile); - fclose(repFile); - */ - - // Export events as text - FILE *repFile = fopen(fileName, "wt"); - - if (repFile != NULL) - { - fprintf(repFile, "# Automation events list\n"); - fprintf(repFile, "# c \n"); - fprintf(repFile, "# e // \n"); - - fprintf(repFile, "c %i\n", eventCount); - for (int i = 0; i < eventCount; i++) - { - fprintf(repFile, "e %i %i %i %i %i // %s\n", events[i].frame, events[i].type, - events[i].params[0], events[i].params[1], events[i].params[2], autoEventTypeName[events[i].type]); - } - - fclose(repFile); - } -} - -// EndDrawing() -> After PollInputEvents() -// Check event in current frame and save into the events[i] array -static void RecordAutomationEvent(unsigned int frame) -{ + // Keyboard input events recording + //------------------------------------------------------------------------------------- for (int key = 0; key < MAX_KEYBOARD_KEYS; key++) { - // INPUT_KEY_UP (only saved once) + // Event type: INPUT_KEY_UP (only saved once) if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_KEY_UP; - events[eventCount].params[0] = key; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_KEY_UP; + currentEventList->events[currentEventList->count].params[0] = key; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_KEY_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_KEY_DOWN + // Event type: INPUT_KEY_DOWN if (CORE.Input.Keyboard.currentKeyState[key]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_KEY_DOWN; - events[eventCount].params[0] = key; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_KEY_DOWN; + currentEventList->events[currentEventList->count].params[0] = key; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_KEY_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check } + //------------------------------------------------------------------------------------- + // Mouse input currentEventList->events recording + //------------------------------------------------------------------------------------- for (int button = 0; button < MAX_MOUSE_BUTTONS; button++) { - // INPUT_MOUSE_BUTTON_UP + // Event type: INPUT_MOUSE_BUTTON_UP if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_BUTTON_UP; - events[eventCount].params[0] = button; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_UP; + currentEventList->events[currentEventList->count].params[0] = button; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_MOUSE_BUTTON_DOWN + // Event type: INPUT_MOUSE_BUTTON_DOWN if (CORE.Input.Mouse.currentButtonState[button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_BUTTON_DOWN; - events[eventCount].params[0] = button; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_DOWN; + currentEventList->events[currentEventList->count].params[0] = button; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check } - // INPUT_MOUSE_POSITION (only saved if changed) + // Event type: INPUT_MOUSE_POSITION (only saved if changed) if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) || ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y)) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_POSITION; - events[eventCount].params[0] = (int)CORE.Input.Mouse.currentPosition.x; - events[eventCount].params[1] = (int)CORE.Input.Mouse.currentPosition.y; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_MOUSE_POSITION; + currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentPosition.x; + currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentPosition.y; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; + + if (currentEventList->count == currentEventList->capacity) return; // Security check } - // INPUT_MOUSE_WHEEL_MOTION + // Event type: INPUT_MOUSE_WHEEL_MOTION if (((int)CORE.Input.Mouse.currentWheelMove.x != (int)CORE.Input.Mouse.previousWheelMove.x) || ((int)CORE.Input.Mouse.currentWheelMove.y != (int)CORE.Input.Mouse.previousWheelMove.y)) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_WHEEL_MOTION; - events[eventCount].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x; - events[eventCount].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_MOUSE_WHEEL_MOTION; + currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x; + currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_WHEEL_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_WHEEL_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; + + if (currentEventList->count == currentEventList->capacity) return; // Security check } + //------------------------------------------------------------------------------------- + // Touch input currentEventList->events recording + //------------------------------------------------------------------------------------- for (int id = 0; id < MAX_TOUCH_POINTS; id++) { - // INPUT_TOUCH_UP + // Event type: INPUT_TOUCH_UP if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_TOUCH_UP; - events[eventCount].params[0] = id; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_TOUCH_UP; + currentEventList->events[currentEventList->count].params[0] = id; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_TOUCH_DOWN + // Event type: INPUT_TOUCH_DOWN if (CORE.Input.Touch.currentTouchState[id]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_TOUCH_DOWN; - events[eventCount].params[0] = id; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_TOUCH_DOWN; + currentEventList->events[currentEventList->count].params[0] = id; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_TOUCH_POSITION + // Event type: INPUT_TOUCH_POSITION // TODO: It requires the id! /* if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) || ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y)) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_TOUCH_POSITION; - events[eventCount].params[0] = id; - events[eventCount].params[1] = (int)CORE.Input.Touch.currentPosition[id].x; - events[eventCount].params[2] = (int)CORE.Input.Touch.currentPosition[id].y; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_TOUCH_POSITION; + currentEventList->events[currentEventList->count].params[0] = id; + currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.currentPosition[id].x; + currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.currentPosition[id].y; - TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } */ + + if (currentEventList->count == currentEventList->capacity) return; // Security check } + //------------------------------------------------------------------------------------- + // Gamepad input currentEventList->events recording + //------------------------------------------------------------------------------------- for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++) { - // INPUT_GAMEPAD_CONNECT + // Event type: INPUT_GAMEPAD_CONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready @@ -3028,7 +3161,7 @@ static void RecordAutomationEvent(unsigned int frame) } */ - // INPUT_GAMEPAD_DISCONNECT + // Event type: INPUT_GAMEPAD_DISCONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready @@ -3039,122 +3172,84 @@ static void RecordAutomationEvent(unsigned int frame) for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++) { - // INPUT_GAMEPAD_BUTTON_UP + // Event type: INPUT_GAMEPAD_BUTTON_UP if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GAMEPAD_BUTTON_UP; - events[eventCount].params[0] = gamepad; - events[eventCount].params[1] = button; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_UP; + currentEventList->events[currentEventList->count].params[0] = gamepad; + currentEventList->events[currentEventList->count].params[1] = button; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_GAMEPAD_BUTTON_DOWN + // Event type: INPUT_GAMEPAD_BUTTON_DOWN if (CORE.Input.Gamepad.currentButtonState[gamepad][button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GAMEPAD_BUTTON_DOWN; - events[eventCount].params[0] = gamepad; - events[eventCount].params[1] = button; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_DOWN; + currentEventList->events[currentEventList->count].params[0] = gamepad; + currentEventList->events[currentEventList->count].params[1] = button; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check } for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++) { - // INPUT_GAMEPAD_AXIS_MOTION + // Event type: INPUT_GAMEPAD_AXIS_MOTION if (CORE.Input.Gamepad.axisState[gamepad][axis] > 0.1f) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GAMEPAD_AXIS_MOTION; - events[eventCount].params[0] = gamepad; - events[eventCount].params[1] = axis; - events[eventCount].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f); + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION; + currentEventList->events[currentEventList->count].params[0] = gamepad; + currentEventList->events[currentEventList->count].params[1] = axis; + currentEventList->events[currentEventList->count].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f); - TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_AXIS_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_AXIS_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check } } + //------------------------------------------------------------------------------------- - // INPUT_GESTURE + // Gestures input currentEventList->events recording + //------------------------------------------------------------------------------------- if (GESTURES.current != GESTURE_NONE) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GESTURE; - events[eventCount].params[0] = GESTURES.current; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + // Event type: INPUT_GESTURE + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_GESTURE; + currentEventList->events[currentEventList->count].params[0] = GESTURES.current; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_GESTURE: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GESTURE | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; + + if (currentEventList->count == currentEventList->capacity) return; // Security check } -} + //------------------------------------------------------------------------------------- -// Play automation event -static void PlayAutomationEvent(unsigned int frame) -{ - for (unsigned int i = 0; i < eventCount; i++) - { - if (events[i].frame == frame) - { - switch (events[i].type) - { - // Input events - case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = false; break; // param[0]: key - case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = true; break; // param[0]: key - case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = false; break; // param[0]: key - case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = true; break; // param[0]: key - case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y - { - CORE.Input.Mouse.currentPosition.x = (float)events[i].params[0]; - CORE.Input.Mouse.currentPosition.y = (float)events[i].params[1]; - } break; - case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta - { - CORE.Input.Mouse.currentWheelMove.x = (float)events[i].params[0]; break; - CORE.Input.Mouse.currentWheelMove.y = (float)events[i].params[1]; break; - } break; - case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[events[i].params[0]] = false; break; // param[0]: id - case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[events[i].params[0]] = true; break; // param[0]: id - case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y - { - CORE.Input.Touch.position[events[i].params[0]].x = (float)events[i].params[1]; - CORE.Input.Touch.position[events[i].params[0]].y = (float)events[i].params[2]; - } break; - case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = true; break; // param[0]: gamepad - case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = false; break; // param[0]: gamepad - case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = false; break; // param[0]: gamepad, param[1]: button - case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = true; break; // param[0]: gamepad, param[1]: button - case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta - { - CORE.Input.Gamepad.axisState[events[i].params[0]][events[i].params[1]] = ((float)events[i].params[2]/32768.0f); - } break; - case INPUT_GESTURE: GESTURES.current = events[i].params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current + // Window events recording + //------------------------------------------------------------------------------------- + // TODO. + //------------------------------------------------------------------------------------- - // Window events - case WINDOW_CLOSE: CORE.Window.shouldClose = true; break; - case WINDOW_MAXIMIZE: MaximizeWindow(); break; - case WINDOW_MINIMIZE: MinimizeWindow(); break; - case WINDOW_RESIZE: SetWindowSize(events[i].params[0], events[i].params[1]); break; - - // Custom events - case ACTION_TAKE_SCREENSHOT: - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } break; - case ACTION_SETTARGETFPS: SetTargetFPS(events[i].params[0]); break; - default: break; - } - } - } + // Custom actions events recording + //------------------------------------------------------------------------------------- + // TODO. + //------------------------------------------------------------------------------------- } #endif diff --git a/src/rcore.h b/src/rcore.h index 1127585a0..a6955f8cc 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -89,6 +89,10 @@ #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB #endif +#ifndef MAX_AUTOMATION_EVENTS + #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record +#endif + // Flags operation macros #define FLAG_SET(n, f) ((n) |= (f)) #define FLAG_CLEAR(n, f) ((n) &= ~(f)) From 654b4e62579aea3dd7dc6ae0018271e2df1c2b55 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 00:45:00 +0200 Subject: [PATCH 0112/1037] Update core_automation_events.c --- examples/core/core_automation_events.c | 85 ++++++++++++++++++-------- 1 file changed, 60 insertions(+), 25 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index d011d6304..c35ce485a 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -77,7 +77,7 @@ int main(void) int frameCounter = 0; int playFrameCounter = 0; - int currentFrame = 0; + int currentPlayFrame = 0; SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -87,7 +87,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - float deltaTime = GetFrameTime(); + float deltaTime = 0.015f;//GetFrameTime(); // Dropped files logic //---------------------------------------------------------------------------------- @@ -106,6 +106,7 @@ int main(void) // Reset scene state to play eventPlaying = true; playFrameCounter = 0; + currentPlayFrame = 0; player.position = (Vector2){ 400, 280 }; player.speed = 0; @@ -163,8 +164,15 @@ int main(void) if (IsKeyPressed(KEY_R)) { - camera.zoom = 1.0f; + // Reset game state player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; } //---------------------------------------------------------------------------------- @@ -193,7 +201,7 @@ int main(void) //---------------------------------------------------------------------------------- // Toggle events recording - if (IsKeyPressed(KEY_ONE)) + if (IsKeyPressed(KEY_F2)) { if (!eventPlaying) { @@ -211,22 +219,41 @@ int main(void) } } } + else if (IsKeyPressed(KEY_F3)) + { + if (!eventRecording && (aelist.count > 0)) + { + // Reset scene state to play + eventPlaying = true; + playFrameCounter = 0; + currentPlayFrame = 0; + + player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; + } + } if (eventPlaying) { - if (playFrameCounter == aelist.events[currentFrame].frame) + if (playFrameCounter >= aelist.events[currentPlayFrame].frame) { - PlayAutomationEvent(aelist.events[currentFrame]); - currentFrame++; + PlayAutomationEvent(aelist.events[currentPlayFrame]); + currentPlayFrame++; - if (currentFrame == aelist.count) + if (currentPlayFrame == aelist.count) { eventPlaying = false; - currentFrame = 0; + currentPlayFrame = 0; playFrameCounter = 0; } } - + playFrameCounter++; } @@ -253,28 +280,36 @@ int main(void) EndMode2D(); + // Draw game controls + DrawRectangle(10, 10, 290, 145, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(10, 10, 290, 145, Fade(BLUE, 0.8f)); + + DrawText("Controls:", 20, 20, 10, BLACK); + DrawText("- RIGHT | LEFT: Player movement", 30, 40, 10, DARKGRAY); + DrawText("- SPACE: Player jump", 30, 60, 10, DARKGRAY); + DrawText("- R: Reset game state", 30, 80, 10, DARKGRAY); + + DrawText("- F2: START/STOP RECORDING INPUT EVENTS", 30, 110, 10, BLACK); + DrawText("- F3: REPLAY LAST RECORDED INPUT EVENTS", 30, 130, 10, BLACK); + // Draw automation events recording indicator if (eventRecording) { - if (((frameCounter/15)%2) == 1) - { - DrawCircle(GetScreenWidth() - 200, 20, 10, MAROON); - DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), GetScreenWidth() - 180, 15, 10, RED); - } + DrawRectangle(10, 160, 290, 30, Fade(RED, 0.3f)); + DrawRectangleLines(10, 160, 290, 30, Fade(MAROON, 0.8f)); + DrawCircle(30, 175, 10, MAROON); + + if (((frameCounter/15)%2) == 1) DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), 50, 170, 10, MAROON); } else if (eventPlaying) { - if (((frameCounter/15)%2) == 1) - { - DrawTriangle((Vector2){ GetScreenWidth() - 200, 10 }, (Vector2){ GetScreenWidth() - 200, 30 }, (Vector2){ GetScreenWidth() - 200 + 20, 20 }, DARKGREEN); - DrawText(TextFormat("PLAYING EVENTS... [%i]", currentFrame), GetScreenWidth() - 170, 15, 10, LIME); - } - } + DrawRectangle(10, 160, 290, 30, Fade(LIME, 0.3f)); + DrawRectangleLines(10, 160, 290, 30, Fade(DARKGREEN, 0.8f)); + DrawTriangle((Vector2){ 20, 155 + 10 }, (Vector2){ 20, 155 + 30 }, (Vector2){ 40, 155 + 20 }, DARKGREEN); - DrawText("Controls:", 20, 20, 10, BLACK); - DrawText("- Right/Left to move", 30, 40, 10, DARKGRAY); - DrawText("- Space to jump", 30, 60, 10, DARKGRAY); - DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 30, 80, 10, DARKGRAY); + if (((frameCounter/15)%2) == 1) DrawText(TextFormat("PLAYING RECORDED EVENTS... [%i]", currentPlayFrame), 50, 170, 10, DARKGREEN); + } + EndDrawing(); //---------------------------------------------------------------------------------- From 98fcbe3fe2bc2f6a20f8c2415251e9164af8661f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 00:50:02 +0200 Subject: [PATCH 0113/1037] Update core_automation_events.c --- examples/core/core_automation_events.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index c35ce485a..adea0e88b 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -241,7 +241,8 @@ int main(void) if (eventPlaying) { - if (playFrameCounter >= aelist.events[currentPlayFrame].frame) + // NOTE: Multiple events could be executed in a single frame + while (playFrameCounter == aelist.events[currentPlayFrame].frame) { PlayAutomationEvent(aelist.events[currentPlayFrame]); currentPlayFrame++; From f721429f2584b2d2769c7031eb8823f4d768d979 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 01:19:10 +0200 Subject: [PATCH 0114/1037] ADDED: `SetAutomationEventBaseFrame(int frame)` --- examples/core/core_automation_events.c | 1 + src/raylib.h | 7 ++++--- src/rcore.c | 6 ++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index adea0e88b..27711b39d 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -214,6 +214,7 @@ int main(void) } else { + SetAutomationEventBaseFrame(180); StartAutomationEventRecording(); eventRecording = true; } diff --git a/src/raylib.h b/src/raylib.h index 7b3eddaee..d8bf1e07c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -508,9 +508,9 @@ typedef struct FilePathList { // Automation event (opaque struct) typedef struct AutomationEvent { - unsigned int frame; // Event frame - unsigned int type; // Event type (AutomationEventType) - int params[4]; // Event parameters (if required) + unsigned int frame; // Event frame + unsigned int type; // Event type (AutomationEventType) + int params[4]; // Event parameters (if required) } AutomationEvent; // Automation event list @@ -1133,6 +1133,7 @@ RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to +RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set) RLAPI void StopAutomationEventRecording(void); // Stop recording automation events RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event diff --git a/src/rcore.c b/src/rcore.c index 67c79efa9..c3b69ae88 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2289,6 +2289,12 @@ void SetAutomationEventList(AutomationEventList *list) #endif } +// Set automation event internal base frame to start recording +void SetAutomationEventBaseFrame(int frame) +{ + CORE.Time.frameCounter = frame; +} + // Start recording automation events (AutomationEventList must be set) void StartAutomationEventRecording(void) { From 3afd0a55b9f243d1a3f0fb6be026deb085828c9d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 16:55:27 +0200 Subject: [PATCH 0115/1037] Update miniaudio to latest dev #3471 --- src/external/miniaudio.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 518e3c43a..ac2da690d 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -2675,9 +2675,16 @@ outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frame example below: ```c - framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite); + ma_uint64 framesWritten; + result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); + if (result != MA_SUCCESS) { + ... handle error ... + } ``` +The `framesWritten` variable will contain the number of PCM frames that were actually written. This +is optionally and you can pass in `NULL` if you need this. + Encoders must be uninitialized with `ma_encoder_uninit()`. @@ -40902,6 +40909,11 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ma_device__on_notification_stopped(pDevice); } + /* If we stopped because the device has been uninitialized, abort now. */ + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + break; + } + /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ ma_device__set_state(pDevice, ma_device_state_stopped); ma_event_signal(&pDevice->stopEvent); @@ -76804,6 +76816,7 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* seekTarget = ma_atomic_load_64(&pSound->seekTarget); if (seekTarget != MA_SEEK_TARGET_NONE) { *pCursor = seekTarget; + return MA_SUCCESS; } else { return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); } From 2db7c727b653fc526b7da07fd337de02b7156e37 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Fri, 27 Oct 2023 12:01:05 -0300 Subject: [PATCH 0116/1037] GetCurrentMonitor() - use closest monitor (#3472) --- src/platforms/rcore_desktop.c | 55 ++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 1114181a4..f60a0e56c 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -751,15 +751,19 @@ int GetCurrentMonitor(void) } else { - int x = 0; - int y = 0; + int closestDist = 0x7FFFFFFF; - glfwGetWindowPos(platform.handle, &x, &y); - x += (int)CORE.Window.screen.width / 2; - y += (int)CORE.Window.screen.height / 2; + // Window center position + int wcx = 0; + int wcy = 0; + + glfwGetWindowPos(platform.handle, &wcx, &wcy); + wcx += (int)CORE.Window.screen.width / 2; + wcy += (int)CORE.Window.screen.height / 2; for (int i = 0; i < monitorCount; i++) { + // Monitor top-left position int mx = 0; int my = 0; @@ -769,17 +773,46 @@ int GetCurrentMonitor(void) if (mode) { - const int width = mode->width; - const int height = mode->height; + const int right = mx + mode->width - 1; + const int bottom = my + mode->height - 1; - if ((x >= mx) && - (x < (mx + width)) && - (y >= my) && - (y < (my + height))) + if ((wcx >= mx) && + (wcx <= right) && + (wcy >= my) && + (wcy <= bottom)) { index = i; break; } + + int xclosest = wcx; + if (wcx < mx) + { + xclosest = mx; + } + else if (wcx > right) + { + xclosest = right; + } + + int yclosest = wcy; + if (wcy < my) + { + yclosest = my; + } + else if (wcy > bottom) + { + yclosest = bottom; + } + + int dx = wcx - xclosest; + int dy = wcy - yclosest; + int dist = (dx * dx) + (dy * dy); + if (dist < closestDist) + { + index = i; + closestDist = dist; + } } else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } From b46505b13d4a85e26d1d5b6f9fc2a4264bf8b02f Mon Sep 17 00:00:00 2001 From: SuperUserNameMan Date: Fri, 27 Oct 2023 17:13:10 +0200 Subject: [PATCH 0117/1037] Update tinyobj_loader_c.h (#3474) temporary quickfix for issue #3473 --- src/external/tinyobj_loader_c.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h index 502a55a7e..55d595a69 100644 --- a/src/external/tinyobj_loader_c.h +++ b/src/external/tinyobj_loader_c.h @@ -1269,6 +1269,11 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, if (is_line_ending(buf, i, end_idx)) { line_infos[line_no].pos = prev_pos; line_infos[line_no].len = i - prev_pos; + +// ---- QUICK BUG FIX : https://github.com/raysan5/raylib/issues/3473 + if ( i > 0 && buf[i-1] == '\r' ) line_infos[line_no].len--; +// -------- + prev_pos = i + 1; line_no++; } From effa3ee249ed393fb7b283092c4023e919be3061 Mon Sep 17 00:00:00 2001 From: Khalid Abdullah Date: Sun, 29 Oct 2023 21:22:59 +0600 Subject: [PATCH 0118/1037] [Typo fixed] in CHANGELOG (#3477) --- CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1da04cc73..244d329da 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1871,9 +1871,9 @@ other changes: [models] Added function DrawCubeTexture() [models] Added function DrawQuad() [models] Added function DrawRay() -[models] Simplified funtion DrawPlane() +[models] Simplified function DrawPlane() [models] Removed function DrawPlaneEx() -[models] Simplified funtion DrawGizmo() +[models] Simplified function DrawGizmo() [models] Removed function DrawGizmoEx() [models] Added function LoadModelEx() [models] Review of function LoadCubicMap() From 01c264123d7bb12d166069a7f5c7403e98e51cfe Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 29 Oct 2023 12:23:38 -0300 Subject: [PATCH 0119/1037] Remove rcore.h include from SDL (#3475) --- src/platforms/rcore_desktop_sdl.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 8543c751c..566d75d8c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -49,8 +49,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #include "SDL.h" // SDL base library (window/rendered, input, timming... functionality) #include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer) @@ -998,7 +996,7 @@ void PollInputEvents(void) switch (event.type) { case SDL_QUIT: CORE.Window.shouldClose = true; break; - + case SDL_DROPFILE: // Dropped file { if (CORE.Window.dropFileCount == 0) @@ -1019,7 +1017,7 @@ void PollInputEvents(void) CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); SDL_free(event.drop.file); - + CORE.Window.dropFileCount++; } else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!"); @@ -1274,7 +1272,7 @@ int InitPlatform(void) SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } - + SDL_EventState(SDL_DROPFILE, SDL_ENABLE); //---------------------------------------------------------------------------- From b4865588f84288b0c52f143a91c6e12908d1237f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 16:36:46 +0100 Subject: [PATCH 0120/1037] REVIEWED: `GetCurrentMonitor()` #3472 --- src/platforms/rcore_desktop.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index f60a0e56c..08b329e85 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -751,6 +751,11 @@ int GetCurrentMonitor(void) } else { + // In case the window is between two monitors, we use below logic + // to try to detect the "current monitor" for that window, note that + // this is probably an overengineered solution for a very side case + // trying to match SDL behaviour + int closestDist = 0x7FFFFFFF; // Window center position @@ -758,8 +763,8 @@ int GetCurrentMonitor(void) int wcy = 0; glfwGetWindowPos(platform.handle, &wcx, &wcy); - wcx += (int)CORE.Window.screen.width / 2; - wcy += (int)CORE.Window.screen.height / 2; + wcx += (int)CORE.Window.screen.width/2; + wcy += (int)CORE.Window.screen.height/2; for (int i = 0; i < monitorCount; i++) { @@ -786,28 +791,16 @@ int GetCurrentMonitor(void) } int xclosest = wcx; - if (wcx < mx) - { - xclosest = mx; - } - else if (wcx > right) - { - xclosest = right; - } + if (wcx < mx) xclosest = mx; + else if (wcx > right) xclosest = right; int yclosest = wcy; - if (wcy < my) - { - yclosest = my; - } - else if (wcy > bottom) - { - yclosest = bottom; - } + if (wcy < my) yclosest = my; + else if (wcy > bottom) yclosest = bottom; int dx = wcx - xclosest; int dy = wcy - yclosest; - int dist = (dx * dx) + (dy * dy); + int dist = (dx*dx) + (dy*dy); if (dist < closestDist) { index = i; From 975d4154e6739fc7fcbe6e3406d4974aa5c4705e Mon Sep 17 00:00:00 2001 From: Josh Colclough Date: Sun, 29 Oct 2023 15:41:02 +0000 Subject: [PATCH 0121/1037] Fix the Julia set shader example (#3467) * Simplify POI selection * Improve mouse logic * Add colour cycles to the shader to show finer details. Works well with high iteration numbers * Testing things... * Actually fix zoom. Also allow user to reset camera with 'R' * Reset max iterations * Tidying & comments * Revert to original if statement * Make mouse logic more readable * Style conventions * Coding conventions - f postifx on floating points * Missed a few f postfixes --- .../resources/shaders/glsl100/julia_set.fs | 32 +++--- .../resources/shaders/glsl330/julia_set.fs | 34 +++--- examples/shaders/shaders_julia_set.c | 102 +++++++++--------- 3 files changed, 89 insertions(+), 79 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/julia_set.fs b/examples/shaders/resources/shaders/glsl100/julia_set.fs index 44d083459..82d0a75ab 100644 --- a/examples/shaders/resources/shaders/glsl100/julia_set.fs +++ b/examples/shaders/resources/shaders/glsl100/julia_set.fs @@ -6,30 +6,30 @@ precision mediump float; varying vec2 fragTexCoord; varying vec4 fragColor; -uniform vec2 screenDims; // Dimensions of the screen uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c uniform vec2 offset; // Offset of the scale. uniform float zoom; // Zoom of the scale. // NOTE: Maximum number of shader for-loop iterations depend on GPU, // for example, on RasperryPi for this examply only supports up to 60 -const int MAX_ITERATIONS = 48; // Max iterations to do +const int maxIterations = 48; // Max iterations to do. +const float colorCycles = 1.0f; // Number of times the color palette repeats. // Square a complex number vec2 ComplexSquare(vec2 z) { return vec2( - z.x * z.x - z.y * z.y, - z.x * z.y * 2.0 + z.x*z.x - z.y*z.y, + z.x*z.y*2.0f ); } // Convert Hue Saturation Value (HSV) color into RGB vec3 Hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + vec4 K = vec4(1.0f, 2.0f/3.0f, 1.0f/3.0f, 3.0f); + vec3 p = abs(fract(c.xxx + K.xyz)*6.0f - K.www); + return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0f, 1.0f), c.y); } void main() @@ -45,8 +45,8 @@ void main() If the number is below 2, we keep iterating. But when do we stop iterating if the number is always below 2 (it converges)? - That is what MAX_ITERATIONS is for. - Then we can divide the iterations by the MAX_ITERATIONS value to get a normalized value that we can + That is what maxIterations is for. + Then we can divide the iterations by the maxIterations value to get a normalized value that we can then map to a color. We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared. @@ -55,13 +55,15 @@ void main() // The pixel coordinates are scaled so they are on the mandelbrot scale // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom - vec2 z = vec2((fragTexCoord.x + offset.x/screenDims.x)*2.5/zoom, (fragTexCoord.y + offset.y/screenDims.y)*1.5/zoom); + vec2 z = vec2((fragTexCoord.x - 0.5f)*2.5f, (fragTexCoord.y - 0.5f)*1.5f)/zoom; + z.x += offset.x; + z.y += offset.y; int iter = 0; for (int iterations = 0; iterations < 60; iterations++) { z = ComplexSquare(z) + c; // Iterate function - if (dot(z, z) > 4.0) break; + if (dot(z, z) > 4.0f) break; iter = iterations; } @@ -72,12 +74,12 @@ void main() z = ComplexSquare(z) + c; // This last part smooths the color (again see link above). - float smoothVal = float(iter) + 1.0 - (log(log(length(z)))/log(2.0)); + float smoothVal = float(iter) + 1.0f - (log(log(length(z)))/log(2.0f)); // Normalize the value so it is between 0 and 1. - float norm = smoothVal/float(MAX_ITERATIONS); + float norm = smoothVal/float(maxIterations); // If in set, color black. 0.999 allows for some float accuracy error. - if (norm > 0.999) gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); - else gl_FragColor = vec4(Hsv2rgb(vec3(norm, 1.0, 1.0)), 1.0); + if (norm > 0.999f) gl_FragColor = vec4(0.0f, 0.0f, 0.0f, 1.0f); + else gl_FragColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0f, 1.0f)), 1.0f); } diff --git a/examples/shaders/resources/shaders/glsl330/julia_set.fs b/examples/shaders/resources/shaders/glsl330/julia_set.fs index c5ee0da60..7a6f069c8 100644 --- a/examples/shaders/resources/shaders/glsl330/julia_set.fs +++ b/examples/shaders/resources/shaders/glsl330/julia_set.fs @@ -7,28 +7,28 @@ in vec4 fragColor; // Output fragment color out vec4 finalColor; -uniform vec2 screenDims; // Dimensions of the screen uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c uniform vec2 offset; // Offset of the scale. uniform float zoom; // Zoom of the scale. -const int MAX_ITERATIONS = 255; // Max iterations to do. +const int maxIterations = 255; // Max iterations to do. +const float colorCycles = 2.0f; // Number of times the color palette repeats. Can show higher detail for higher iteration numbers. // Square a complex number vec2 ComplexSquare(vec2 z) { return vec2( - z.x * z.x - z.y * z.y, - z.x * z.y * 2.0 + z.x*z.x - z.y*z.y, + z.x*z.y*2.0f ); } // Convert Hue Saturation Value (HSV) color into RGB vec3 Hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + vec4 K = vec4(1.0f, 2.0f/3.0f, 1.0f/3.0f, 3.0f); + vec3 p = abs(fract(c.xxx + K.xyz)*6.0f - K.www); + return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0f, 1.0f), c.y); } void main() @@ -44,8 +44,8 @@ void main() If the number is below 2, we keep iterating. But when do we stop iterating if the number is always below 2 (it converges)? - That is what MAX_ITERATIONS is for. - Then we can divide the iterations by the MAX_ITERATIONS value to get a normalized value that we can + That is what maxIterations is for. + Then we can divide the iterations by the maxIterations value to get a normalized value that we can then map to a color. We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared. @@ -54,14 +54,16 @@ void main() // The pixel coordinates are scaled so they are on the mandelbrot scale // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom - vec2 z = vec2((fragTexCoord.x + offset.x/screenDims.x)*2.5/zoom, (fragTexCoord.y + offset.y/screenDims.y)*1.5/zoom); + vec2 z = vec2((fragTexCoord.x - 0.5f)*2.5f, (fragTexCoord.y - 0.5f)*1.5f)/zoom; + z.x += offset.x; + z.y += offset.y; int iterations = 0; - for (iterations = 0; iterations < MAX_ITERATIONS; iterations++) + for (iterations = 0; iterations < maxIterations; iterations++) { z = ComplexSquare(z) + c; // Iterate function - if (dot(z, z) > 4.0) break; + if (dot(z, z) > 4.0f) break; } // Another few iterations decreases errors in the smoothing calculation. @@ -70,12 +72,12 @@ void main() z = ComplexSquare(z) + c; // This last part smooths the color (again see link above). - float smoothVal = float(iterations) + 1.0 - (log(log(length(z)))/log(2.0)); + float smoothVal = float(iterations) + 1.0f - (log(log(length(z)))/log(2.0f)); // Normalize the value so it is between 0 and 1. - float norm = smoothVal/float(MAX_ITERATIONS); + float norm = smoothVal/float(maxIterations); // If in set, color black. 0.999 allows for some float accuracy error. - if (norm > 0.999) finalColor = vec4(0.0, 0.0, 0.0, 1.0); - else finalColor = vec4(Hsv2rgb(vec3(norm, 1.0, 1.0)), 1.0); + if (norm > 0.999f) finalColor = vec4(0.0f, 0.0f, 0.0f, 1.0f); + else finalColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0f, 1.0f)), 1.0f); } diff --git a/examples/shaders/shaders_julia_set.c b/examples/shaders/shaders_julia_set.c index aebb287a1..608d3b52b 100644 --- a/examples/shaders/shaders_julia_set.c +++ b/examples/shaders/shaders_julia_set.c @@ -9,12 +9,12 @@ * * Example originally created with raylib 2.5, last time updated with raylib 4.0 * -* Example contributed by eggmund (@eggmund) and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by Josh Colclough (@joshcol9232) and reviewed by Ramon Santamaria (@raysan5) * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2019-2023 eggmund (@eggmund) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Josh Colclough (@joshcol9232) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -37,6 +37,13 @@ const float pointsOfInterest[6][2] = { -0.70176f, -0.3842f }, }; +const int screenWidth = 800; +const int screenHeight = 450; +const float zoomSpeed = 1.01f; +const float offsetSpeedMul = 2.0f; + +const float startingZoom = 0.75f; + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -44,10 +51,6 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - //SetConfigFlags(FLAG_WINDOW_HIGHDPI); InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia sets"); // Load julia set shader @@ -61,10 +64,8 @@ int main(void) float c[2] = { pointsOfInterest[0][0], pointsOfInterest[0][1] }; // Offset and zoom to draw the julia set at. (centered on screen and default size) - float offset[2] = { -(float)GetScreenWidth()/2, -(float)GetScreenHeight()/2 }; - float zoom = 1.0f; - - Vector2 offsetSpeed = { 0.0f, 0.0f }; + float offset[2] = { 0.0f, 0.0f }; + float zoom = startingZoom; // Get variable (uniform) locations on the shader to connect with the program // NOTE: If uniform variable could not be found in the shader, function returns -1 @@ -72,17 +73,13 @@ int main(void) int zoomLoc = GetShaderLocation(shader, "zoom"); int offsetLoc = GetShaderLocation(shader, "offset"); - // Tell the shader what the screen dimensions, zoom, offset and c are - float screenDims[2] = { (float)GetScreenWidth(), (float)GetScreenHeight() }; - SetShaderValue(shader, GetShaderLocation(shader, "screenDims"), screenDims, SHADER_UNIFORM_VEC2); - + // Upload the shader uniform values! SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2); int incrementSpeed = 0; // Multiplier of speed to change c value bool showControls = true; // Show controls - bool pause = false; // Pause animation SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -110,42 +107,50 @@ int main(void) SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); } - if (IsKeyPressed(KEY_SPACE)) pause = !pause; // Pause animation (c change) - if (IsKeyPressed(KEY_F1)) showControls = !showControls; // Toggle whether or not to show controls - - if (!pause) + // If "R" is pressed, reset zoom and offset. + if (IsKeyPressed(KEY_R)) { - if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++; - else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--; - - // TODO: The idea is to zoom and move around with mouse - // Probably offset movement should be proportional to zoom level - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) - { - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) zoom += zoom*0.003f; - if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) zoom -= zoom*0.003f; - - Vector2 mousePos = GetMousePosition(); - - offsetSpeed.x = mousePos.x -(float)screenWidth/2; - offsetSpeed.y = mousePos.y -(float)screenHeight/2; - - // Slowly move camera to targetOffset - offset[0] += GetFrameTime()*offsetSpeed.x*0.8f; - offset[1] += GetFrameTime()*offsetSpeed.y*0.8f; - } - else offsetSpeed = (Vector2){ 0.0f, 0.0f }; - + zoom = startingZoom; + offset[0] = 0.0f; + offset[1] = 0.0f; SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2); - - // Increment c value with time - float amount = GetFrameTime()*incrementSpeed*0.0005f; - c[0] += amount; - c[1] += amount; - - SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); } + + if (IsKeyPressed(KEY_SPACE)) incrementSpeed = 0; // Pause animation (c change) + if (IsKeyPressed(KEY_F1)) showControls = !showControls; // Toggle whether or not to show controls + + if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++; + else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--; + + // If either left or right button is pressed, zoom in/out. + if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) + { + // Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out. + zoom *= IsMouseButtonDown(MOUSE_BUTTON_LEFT)? zoomSpeed : 1.0f/zoomSpeed; + + const Vector2 mousePos = GetMousePosition(); + Vector2 offsetVelocity; + // Find the velocity at which to change the camera. Take the distance of the mouse + // from the center of the screen as the direction, and adjust magnitude based on + // the current zoom. + offsetVelocity.x = (mousePos.x/(float)screenWidth - 0.5f)*offsetSpeedMul/zoom; + offsetVelocity.y = (mousePos.y/(float)screenHeight - 0.5f)*offsetSpeedMul/zoom; + + // Apply move velocity to camera + offset[0] += GetFrameTime()*offsetVelocity.x; + offset[1] += GetFrameTime()*offsetVelocity.y; + + // Update the shader uniform values! + SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT); + SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2); + } + + // Increment c value with time + const float dc = GetFrameTime()*(float)incrementSpeed*0.0005f; + c[0] += dc; + c[1] += dc; + SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); //---------------------------------------------------------------------------------- // Draw @@ -178,7 +183,8 @@ int main(void) DrawText("Press KEY_F1 to toggle these controls", 10, 30, 10, RAYWHITE); DrawText("Press KEYS [1 - 6] to change point of interest", 10, 45, 10, RAYWHITE); DrawText("Press KEY_LEFT | KEY_RIGHT to change speed", 10, 60, 10, RAYWHITE); - DrawText("Press KEY_SPACE to pause movement animation", 10, 75, 10, RAYWHITE); + DrawText("Press KEY_SPACE to stop movement animation", 10, 75, 10, RAYWHITE); + DrawText("Press KEY_R to recenter the camera", 10, 90, 10, RAYWHITE); } EndDrawing(); //---------------------------------------------------------------------------------- From e3363e9ad0904f87cf038d7f7eeb379d988678c3 Mon Sep 17 00:00:00 2001 From: Khalid Abdullah Date: Mon, 30 Oct 2023 00:34:50 +0600 Subject: [PATCH 0122/1037] Typo fixed in HISTORY.md (#3481) * [Typo fixed] in CHANGELOG * [Typo fixed] in HISTORY.md --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 0a0b1ce2b..10f0dfb77 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -140,7 +140,7 @@ notes on raylib 1.7 On May 2017, around 6 month after raylib 1.6, comes another raylib instalment, raylib 1.7. This time library has been improved a lot in terms of consistency and cleanness. As stated in [this patreon article](https://www.patreon.com/posts/raylib-future-7501034), this new raylib version has focused efforts in becoming more simple and easy-to-use to learn videogames programming. Some highlights of this new version are: - - More than 30 new functions added to the library, functions to control Window, utils to work with filenames and extensions, functions to draw lines with custom thick, mesh loading, functions for 3d ray collisions detailed detection, funtions for VR simulation and much more... Just check [CHANGELOG](CHANGELOG) for a detailed list of additions! + - More than 30 new functions added to the library, functions to control Window, utils to work with filenames and extensions, functions to draw lines with custom thick, mesh loading, functions for 3d ray collisions detailed detection, functions for VR simulation and much more... Just check [CHANGELOG](CHANGELOG) for a detailed list of additions! - Support of [configuration flags](https://github.com/raysan5/raylib/issues/200) on every raylib module. Advance users can customize raylib just choosing desired features, defining some configuration flags on modules compilation. That way users can control library size and available functionality. From 2da8cc383cba42af830f251912263db6eb9298fd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 19:46:34 +0100 Subject: [PATCH 0123/1037] Update Makefile.Web --- examples/Makefile.Web | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 3512f7407..9c2cadc37 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -138,7 +138,7 @@ endif # Define default make program: MAKE #------------------------------------------------------------------------------------------------ -MAKE ?= make +MAKE ?= emmake make ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),WINDOWS) From 12f3bc10c2def80abfc76c8158785ac91c0ca1d1 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:20:19 -0300 Subject: [PATCH 0124/1037] [core] Move `rcore.h` content to inside `rcore.c` (#3479) * Move rcore.h content inside rcore.c * Remove extern CoreData CORE --- src/rcore.c | 200 +++++++++++++++++++++++++++++++++++++++++++----- src/rcore.h | 213 ---------------------------------------------------- 2 files changed, 181 insertions(+), 232 deletions(-) delete mode 100644 src/rcore.h diff --git a/src/rcore.c b/src/rcore.c index c3b69ae88..b2eb78675 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -85,12 +85,19 @@ #include "config.h" // Defines module configuration flags #endif -#include "rcore.h" // Defines types and globals +#include "utils.h" // Required for: TRACELOG() macros + +#include // Required for: srand(), rand(), atexit() +#include // Required for: sprintf() [Used in OpenURL()] +#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: time() [Used in InitTimer()] +#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] #define RLGL_IMPLEMENTATION #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 -#include "raymath.h" // Vector3, Quaternion and Matrix functionality +#define RAYMATH_IMPLEMENTATION +#include "raymath.h" // Vector2, Vector3, Quaternion and Matrix functionality #if defined(SUPPORT_GESTURES_SYSTEM) #define RGESTURES_IMPLEMENTATION @@ -166,6 +173,161 @@ __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigne #define CHDIR chdir #endif +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef MAX_FILEPATH_CAPACITY + #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath +#endif +#ifndef MAX_FILEPATH_LENGTH + #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) +#endif + +#ifndef MAX_KEYBOARD_KEYS + #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported +#endif +#ifndef MAX_MOUSE_BUTTONS + #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported +#endif +#ifndef MAX_GAMEPADS + #define MAX_GAMEPADS 4 // Maximum number of gamepads supported +#endif +#ifndef MAX_GAMEPAD_AXIS + #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) +#endif +#ifndef MAX_GAMEPAD_BUTTONS + #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) +#endif +#ifndef MAX_TOUCH_POINTS + #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported +#endif +#ifndef MAX_KEY_PRESSED_QUEUE + #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue +#endif +#ifndef MAX_CHAR_PRESSED_QUEUE + #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue +#endif + +#ifndef MAX_DECOMPRESSION_SIZE + #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB +#endif + +#ifndef MAX_AUTOMATION_EVENTS + #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record +#endif + +// Flags operation macros +#define FLAG_SET(n, f) ((n) |= (f)) +#define FLAG_CLEAR(n, f) ((n) &= ~(f)) +#define FLAG_TOGGLE(n, f) ((n) ^= (f)) +#define FLAG_CHECK(n, f) ((n) & (f)) + +#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { int x; int y; } Point; +typedef struct { unsigned int width; unsigned int height; } Size; + +// Core global state context data +typedef struct CoreData { + struct { + const char *title; // Window text title const pointer + unsigned int flags; // Configuration flags (bit based), keeps window state + bool ready; // Check if window has been initialized successfully + bool fullscreen; // Check if fullscreen mode is enabled + bool shouldClose; // Check if window set for closing + bool resizedLastFrame; // Check if window has been resized last frame + bool eventWaiting; // Wait for events before ending frame + + Point position; // Window position (required on fullscreen toggle) + Point previousPosition; // Window previous position (required on borderless windowed toggle) + Size display; // Display width and height (monitor, device-screen, LCD, ...) + Size screen; // Screen width and height (used render area) + Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) + Size currentFbo; // Current render width and height (depends on active fbo) + Size render; // Framebuffer width and height (render area, including black bars if required) + Point renderOffset; // Offset from render area (must be divided by 2) + Size screenMin; // Screen minimum width and height (for resizable window) + Size screenMax; // Screen maximum width and height (for resizable window) + Matrix screenScale; // Matrix to scale screen (framebuffer rendering) + + char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) + unsigned int dropFileCount; // Count dropped files strings + + } Window; + struct { + const char *basePath; // Base path for data storage + + } Storage; + struct { + struct { + int exitKey; // Default exit key + char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state + char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + + // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially + char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. + + int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue + int keyPressedQueueCount; // Input keys queue count + + int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) + int charPressedQueueCount; // Input characters queue count + + } Keyboard; + struct { + Vector2 offset; // Mouse offset + Vector2 scale; // Mouse scaling + Vector2 currentPosition; // Mouse position on screen + Vector2 previousPosition; // Previous mouse position + + int cursor; // Tracks current mouse cursor + bool cursorHidden; // Track if cursor is hidden + bool cursorOnScreen; // Tracks if cursor is inside client area + + char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state + char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state + Vector2 currentWheelMove; // Registers current mouse wheel variation + Vector2 previousWheelMove; // Registers previous mouse wheel variation + + } Mouse; + struct { + int pointCount; // Number of touch points active + int pointId[MAX_TOUCH_POINTS]; // Point identifiers + Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen + char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state + char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state + + } Touch; + struct { + int lastButtonPressed; // Register last gamepad button pressed + int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis + bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready + char name[MAX_GAMEPADS][64]; // Gamepad name holder + char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state + char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state + float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state + + } Gamepad; + } Input; + struct { + double current; // Current time measure + double previous; // Previous time measure + double update; // Time measure for frame update + 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 + unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) + unsigned int frameCounter; // Frame counter + + } Time; +} CoreData; + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -2156,7 +2318,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName) /* //int dataSize = 0; //unsigned char *data = LoadFileData(fileName, &dataSize); - + FILE *raeFile = fopen(fileName, "rb"); unsigned char fileId[4] = { 0 }; @@ -2202,7 +2364,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName) fgets(buffer, 256, raeFile); } - if (counter != list.count) + if (counter != list.count) { TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count); list.count = counter; @@ -2234,7 +2396,7 @@ void UnloadAutomationEventList(AutomationEventList *list) bool ExportAutomationEventList(AutomationEventList list, const char *fileName) { bool success = false; - + #if defined(SUPPORT_AUTOMATION_EVENTS) // Export events as binary file // TODO: Save to memory buffer and SaveFileData() @@ -2992,7 +3154,7 @@ static void RecordAutomationEvent(void) { // Checking events in current frame and save them into currentEventList // TODO: How important is the current frame? Could it be modified? - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Keyboard input events recording @@ -3011,7 +3173,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_KEY_DOWN @@ -3026,7 +3188,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check } //------------------------------------------------------------------------------------- @@ -3047,7 +3209,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_MOUSE_BUTTON_DOWN @@ -3062,7 +3224,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check } @@ -3078,7 +3240,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; - + if (currentEventList->count == currentEventList->capacity) return; // Security check } @@ -3094,7 +3256,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_WHEEL_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; - + if (currentEventList->count == currentEventList->capacity) return; // Security check } //------------------------------------------------------------------------------------- @@ -3115,7 +3277,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_TOUCH_DOWN @@ -3130,7 +3292,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_TOUCH_POSITION @@ -3149,7 +3311,7 @@ static void RecordAutomationEvent(void) currentEventList->count++; } */ - + if (currentEventList->count == currentEventList->capacity) return; // Security check } //------------------------------------------------------------------------------------- @@ -3190,7 +3352,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_GAMEPAD_BUTTON_DOWN @@ -3205,7 +3367,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check } @@ -3223,7 +3385,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_AXIS_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check } } @@ -3242,7 +3404,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GESTURE | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; - + if (currentEventList->count == currentEventList->capacity) return; // Security check } //------------------------------------------------------------------------------------- diff --git a/src/rcore.h b/src/rcore.h deleted file mode 100644 index a6955f8cc..000000000 --- a/src/rcore.h +++ /dev/null @@ -1,213 +0,0 @@ -/********************************************************************************************** -* -* rcore - Common types and globals (all platforms) -* -* LIMITATIONS: -* - Limitation 01 -* - Limitation 02 -* -* POSSIBLE IMPROVEMENTS: -* - Improvement 01 -* - Improvement 02 -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2013-2023 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. -* -**********************************************************************************************/ - -#ifndef RCORE_H -#define RCORE_H - -#include "raylib.h" - -#include "utils.h" // Required for: TRACELOG() macros - -#include "rlgl.h" // Required for: graphics layer functionality - -#define RAYMATH_IMPLEMENTATION -#include "raymath.h" // Required for: Vector2/Vector3/Matrix functionality - -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() -#include // Required for: time() [Used in InitTimer()] -#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef MAX_FILEPATH_CAPACITY - #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath -#endif -#ifndef MAX_FILEPATH_LENGTH - #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) -#endif - -#ifndef MAX_KEYBOARD_KEYS - #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported -#endif -#ifndef MAX_MOUSE_BUTTONS - #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported -#endif -#ifndef MAX_GAMEPADS - #define MAX_GAMEPADS 4 // Maximum number of gamepads supported -#endif -#ifndef MAX_GAMEPAD_AXIS - #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) -#endif -#ifndef MAX_GAMEPAD_BUTTONS - #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) -#endif -#ifndef MAX_TOUCH_POINTS - #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported -#endif -#ifndef MAX_KEY_PRESSED_QUEUE - #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue -#endif -#ifndef MAX_CHAR_PRESSED_QUEUE - #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue -#endif - -#ifndef MAX_DECOMPRESSION_SIZE - #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB -#endif - -#ifndef MAX_AUTOMATION_EVENTS - #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record -#endif - -// Flags operation macros -#define FLAG_SET(n, f) ((n) |= (f)) -#define FLAG_CLEAR(n, f) ((n) &= ~(f)) -#define FLAG_TOGGLE(n, f) ((n) ^= (f)) -#define FLAG_CHECK(n, f) ((n) & (f)) - -#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) - #undef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -typedef struct { int x; int y; } Point; -typedef struct { unsigned int width; unsigned int height; } Size; - -// Core global state context data -typedef struct CoreData { - struct { - const char *title; // Window text title const pointer - unsigned int flags; // Configuration flags (bit based), keeps window state - bool ready; // Check if window has been initialized successfully - bool fullscreen; // Check if fullscreen mode is enabled - bool shouldClose; // Check if window set for closing - bool resizedLastFrame; // Check if window has been resized last frame - bool eventWaiting; // Wait for events before ending frame - - Point position; // Window position (required on fullscreen toggle) - Point previousPosition; // Window previous position (required on borderless windowed toggle) - Size display; // Display width and height (monitor, device-screen, LCD, ...) - Size screen; // Screen width and height (used render area) - Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) - Size currentFbo; // Current render width and height (depends on active fbo) - Size render; // Framebuffer width and height (render area, including black bars if required) - Point renderOffset; // Offset from render area (must be divided by 2) - Size screenMin; // Screen minimum width and height (for resizable window) - Size screenMax; // Screen maximum width and height (for resizable window) - Matrix screenScale; // Matrix to scale screen (framebuffer rendering) - - char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) - unsigned int dropFileCount; // Count dropped files strings - - } Window; - struct { - const char *basePath; // Base path for data storage - - } Storage; - struct { - struct { - int exitKey; // Default exit key - char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state - char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state - - // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially - char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. - - int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue - int keyPressedQueueCount; // Input keys queue count - - int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) - int charPressedQueueCount; // Input characters queue count - - } Keyboard; - struct { - Vector2 offset; // Mouse offset - Vector2 scale; // Mouse scaling - Vector2 currentPosition; // Mouse position on screen - Vector2 previousPosition; // Previous mouse position - - int cursor; // Tracks current mouse cursor - bool cursorHidden; // Track if cursor is hidden - bool cursorOnScreen; // Tracks if cursor is inside client area - - char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state - char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state - Vector2 currentWheelMove; // Registers current mouse wheel variation - Vector2 previousWheelMove; // Registers previous mouse wheel variation - - } Mouse; - struct { - int pointCount; // Number of touch points active - int pointId[MAX_TOUCH_POINTS]; // Point identifiers - Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen - char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state - char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state - - } Touch; - struct { - int lastButtonPressed; // Register last gamepad button pressed - int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis - bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready - char name[MAX_GAMEPADS][64]; // Gamepad name holder - char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state - char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state - float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state - - } Gamepad; - } Input; - struct { - double current; // Current time measure - double previous; // Previous time measure - double update; // Time measure for frame update - 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 - unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) - unsigned int frameCounter; // Frame counter - - } Time; -} CoreData; - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -extern CoreData CORE; - -#endif From 1fd61a00e4423c68f2b32c46148847b27a6eb94f Mon Sep 17 00:00:00 2001 From: JaanDev <67502867+JaanDev@users.noreply.github.com> Date: Sun, 29 Oct 2023 22:21:00 +0300 Subject: [PATCH 0125/1037] Fix compressed DDS texture loading issues (#3483) --- src/external/rl_gputex.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index fa39fe29c..fb7b5e8de 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -261,11 +261,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ } else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed { - int data_size = 0; - - // Calculate data size, including all mipmaps - if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size*2; - else data_size = header->pitch_or_linear_size; + // NOTE: This forces only 1 mipmap to be loaded which is not really correct but it works + int data_size = (header->pitch_or_linear_size < file_size - 0x80) ? header->pitch_or_linear_size : file_size - 0x80; + *mips = 1; image_data = RL_MALLOC(data_size*sizeof(unsigned char)); From 21243c82344a132c23fc7fe24094a2f4933ab92a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:42:29 +0100 Subject: [PATCH 0126/1037] Update rcore_desktop_sdl.c --- src/platforms/rcore_desktop_sdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 566d75d8c..8793e6c77 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1269,8 +1269,8 @@ int InitPlatform(void) //---------------------------------------------------------------------------- if (SDL_NumJoysticks() >= 1) { - SDL_Joystick *gamepad = SDL_JoystickOpen(0); - //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); + platform.gamepad = SDL_JoystickOpen(0); + //if (platform.gamepadgamepad == NULL) TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError()); } SDL_EventState(SDL_DROPFILE, SDL_ENABLE); From 601e391b068c9bc043b801486e02586012d58f20 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:43:52 +0100 Subject: [PATCH 0127/1037] Remove physac library from raylib building At this moment, physac is an external unmaintained library, better move out of raylib. --- src/Makefile | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/Makefile b/src/Makefile index c9094f096..d5726f5ed 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,13 +78,10 @@ RAYLIB_CONFIG_FLAGS ?= NONE RAYLIB_MODULE_AUDIO ?= TRUE RAYLIB_MODULE_MODELS ?= TRUE RAYLIB_MODULE_RAYGUI ?= FALSE -RAYLIB_MODULE_PHYSAC ?= FALSE # NOTE: Additional libraries have been moved to their own repos: # raygui: https://github.com/raysan5/raygui -# physac: https://github.com/raysan5/physac RAYLIB_MODULE_RAYGUI_PATH ?= $(RAYLIB_SRC_PATH)/../../raygui/src -RAYLIB_MODULE_PHYSAC_PATH ?= $(RAYLIB_SRC_PATH)/../../physac/src # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE @@ -553,9 +550,6 @@ endif ifeq ($(RAYLIB_MODULE_RAYGUI),TRUE) OBJS += raygui.o endif -ifeq ($(RAYLIB_MODULE_PHYSAC),TRUE) - OBJS += physac.o -endif ifeq ($(PLATFORM),PLATFORM_ANDROID) OBJS += android_native_app_glue.o @@ -680,23 +674,10 @@ else @echo "#include \"$(RAYLIB_MODULE_RAYGUI_PATH)/raygui.h\"" >> raygui.c endif -# Compile physac module -# NOTE: physac header should be distributed with raylib.h -physac.o : physac.c - $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -physac.c: -ifeq ($(PLATFORM_SHELL), cmd) - @echo #define PHYSAC_IMPLEMENTATION > physac.c - @echo #include "$(RAYLIB_MODULE_PHYSAC_PATH)/physac.h" >> physac.c -else - @echo "#define PHYSAC_IMPLEMENTATION" > physac.c - @echo "#include \"$(RAYLIB_MODULE_PHYSAC_PATH)/physac.h\"" >> physac.c -endif # Compile android_native_app_glue module android_native_app_glue.o : $(NATIVE_APP_GLUE)/android_native_app_glue.c $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) - # Install generated and needed files to desired directories. # On GNU/Linux and BSDs, there are some standard directories that contain extra # libraries and header files. These directories (often /usr/local/lib and @@ -780,7 +761,7 @@ clean: clean_shell_$(PLATFORM_SHELL) @echo "removed all generated files!" clean_shell_sh: - rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c physac.c + rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c ifeq ($(PLATFORM),PLATFORM_ANDROID) rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o endif @@ -794,4 +775,3 @@ clean_shell_cmd: del lib$(RAYLIB_LIB_NAME)dll.a /s & \ del $(RAYLIB_LIB_NAME).dll /s & \ del raygui.c /s & \ - del physac.c /s From 4625c414319fbcb06fc5f1b633b8cf31c563522c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:44:18 +0100 Subject: [PATCH 0128/1037] ADDED: Support for SDL building on Makefile --- examples/Makefile | 74 ++++++++++++++++++++++++++++++++++++++--- src/Makefile | 84 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 128 insertions(+), 30 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 136a0ab47..d10a35be4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,6 +1,25 @@ #************************************************************************************************** # -# raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 +# raylib makefile for multiple platforms +# +# This file supports building raylib examples for the following platforms: +# +# - PLATFORM_DESKTOP (GLFW backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > macOS/OSX (x64, arm64) +# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# - PLATFORM_DESKTOP_SDL (SDL backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > Others (not tested) +# - PLATFORM_WEB: +# > HTML5 (WebAssembly) +# - PLATFORM_DRM: +# > Raspberry Pi 0-5 (no X11/Wayland) +# > Linux native mode (KMS driver) +# - PLATFORM_ANDROID: +# > Android (ARM, ARM64) # # Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) # @@ -25,7 +44,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DESKTOP_SDL, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables @@ -47,6 +66,11 @@ BUILD_MODE ?= RELEASE # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE +# PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally +# WARNING: Library is not included in raylib, it MUST be configured by users +SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/include +SDL_LIBRARY_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/lib/x64 + # Use Wayland display server protocol on Linux desktop (by default it uses X11 windowing system) # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE @@ -58,8 +82,8 @@ BUILD_WEB_HEAP_SIZE ?= 134217728 BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources -# Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) +# Determine PLATFORM_OS when required +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -224,6 +248,9 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) +endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) INCLUDE_PATHS += -I/usr/include/libdrm @@ -254,6 +281,17 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Lsrc -L$(RAYLIB_LIB_PATH) endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + # NOTE: The resource .rc file contains windows executable icon and properties + LDFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data + # -Wl,--subsystem,windows hides the console window + ifeq ($(BUILD_MODE), RELEASE) + LDFLAGS += -Wl,--subsystem,windows + endif + endif + LDFLAGS += -L$(SDL_LIBRARY_PATH) +endif ifeq ($(PLATFORM),PLATFORM_WEB) # -Os # size optimization # -O2 # optimization level 2, if used, also set --memory-init-file 0 @@ -346,6 +384,34 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lglfw endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + # Libraries for Windows desktop compilation + LDLIBS = -lraylib -lSDL2 -lSDL2main -lopengl32 -lgdi32 + endif + ifeq ($(PLATFORM_OS),LINUX) + # Libraries for Debian GNU/Linux desktop compiling + # NOTE: Required packages: libegl1-mesa-dev + LDLIBS = -lraylib -lSDL2 -lSDL2main -lGL -lm -lpthread -ldl -lrt + + # On X11 requires also below libraries + LDLIBS += -lX11 + # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them + #LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor + + # On Wayland windowing system, additional libraries requires + ifeq ($(USE_WAYLAND_DISPLAY),TRUE) + LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon + endif + # Explicit link to libc + ifeq ($(RAYLIB_LIBTYPE),SHARED) + LDLIBS += -lc + endif + + # NOTE: On ARM 32bit arch, miniaudio requires atomics library + LDLIBS += -latomic + endif +endif ifeq ($(PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) diff --git a/src/Makefile b/src/Makefile index d5726f5ed..2406588b8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,18 +1,28 @@ #****************************************************************************** # -# raylib makefile +# raylib makefile # -# Platforms supported: -# PLATFORM_DESKTOP: Windows (Win32, Win64) -# PLATFORM_DESKTOP: Linux (arm64, i386, x64) -# PLATFORM_DESKTOP: OSX/macOS (arm64, x86_64) -# PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly -# PLATFORM_ANDROID: Android (arm, i686, arm64, x86_64) -# PLATFORM_DRM: Linux native mode, including Raspberry Pi (RPI OS Bullseye) -# PLATFORM_WEB: HTML5 (Chrome, Firefox) +# This file supports building raylib library for the following platforms: # -# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. -# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. +# - PLATFORM_DESKTOP (GLFW backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > macOS/OSX (x64, arm64) +# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# - PLATFORM_DESKTOP_SDL (SDL backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > Others (not tested) +# - PLATFORM_WEB: +# > HTML5 (WebAssembly) +# - PLATFORM_DRM: +# > Raspberry Pi 0-5 (no X11/Wayland) +# > Linux native mode (KMS driver) +# - PLATFORM_ANDROID: +# > Android (ARM, ARM64) +# +# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. +# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. # # Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) # @@ -86,6 +96,11 @@ RAYLIB_MODULE_RAYGUI_PATH ?= $(RAYLIB_SRC_PATH)/../../raygui/src # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE +# PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally +# WARNING: Library is not included in raylib, it MUST be configured by users +SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/include +SDL_LIBRARY_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/lib/x64 + # Use Wayland display server protocol on Linux desktop (by default it uses X11 windowing system) # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE @@ -98,8 +113,8 @@ ROOT = $(shell whoami) HOST_PLATFORM_OS ?= WINDOWS PLATFORM_OS ?= WINDOWS -# Determine PLATFORM_OS in case PLATFORM_DESKTOP selected -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +# Determine PLATFORM_OS when required +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -142,16 +157,7 @@ ifeq ($(PLATFORM),PLATFORM_DRM) endif endif ifeq ($(PLATFORM),PLATFORM_WEB) - ifeq ($(OS),Windows_NT) - PLATFORM_OS = WINDOWS - ifndef PLATFORM_SHELL - PLATFORM_SHELL = cmd - endif - else - UNAMEOS = $(shell uname) - ifeq ($(UNAMEOS),Linux) - PLATFORM_OS = LINUX - endif + ifeq ($(PLATFORM_OS),LINUX) ifndef PLATFORM_SHELL PLATFORM_SHELL = sh endif @@ -214,7 +220,10 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif - +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + # By default use OpenGL 3.3 on desktop platform with SDL backend + GRAPHICS ?= GRAPHICS_API_OPENGL_33 +endif ifeq ($(PLATFORM),PLATFORM_DRM) # On DRM OpenGL ES 2.0 must be used GRAPHICS = GRAPHICS_API_OPENGL_ES2 @@ -413,14 +422,21 @@ endif # Define include paths for required headers: INCLUDE_PATHS # NOTE: Several external required libraries (stb and others) #------------------------------------------------------------------------------------------------ -INCLUDE_PATHS = -I. -Iexternal/glfw/include -Iexternal/glfw/deps/mingw +INCLUDE_PATHS = -I. # Define additional directories containing required header files ifeq ($(PLATFORM),PLATFORM_DESKTOP) + INCLUDE_PATHS += -Iexternal/glfw/include -Iexternal/glfw/deps/mingw ifeq ($(PLATFORM_OS),BSD) INCLUDE_PATHS += -I/usr/local/include endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + INCLUDE_PATHS += -Iexternal/glfw/include -Iexternal/glfw/deps/mingw +endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) @@ -470,10 +486,14 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) + LDFLAGS += -L$(SDL_LIBRARY_PATH) +endif ifeq ($(PLATFORM),PLATFORM_DRM) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) - INCLUDE_PATHS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib + LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) @@ -518,6 +538,18 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS = -lglfw endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + LDLIBS = -static-libgcc -lopengl32 -lgdi32 + endif + ifeq ($(PLATFORM_OS),LINUX) + LDLIBS = -lGL -lc -lm -lpthread -ldl -lrt + ifeq ($(USE_WAYLAND_DISPLAY),FALSE) + LDLIBS += -lX11 + endif + endif + LDLIBS += -lSDL2 -lSDL2main +endif ifeq ($(PLATFORM),PLATFORM_DRM) LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) From 09075d515af4fcf3caca8c494e7bcf71d64060da Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:44:32 +0100 Subject: [PATCH 0129/1037] Some notes and comments --- src/rcore.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b2eb78675..1a680ee9a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,11 +3,15 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP: +* - PLATFORM_DESKTOP (GLFW backend): * > Windows (Win32, Win64) * > Linux (X11/Wayland desktop mode) * > macOS/OSX (x64, arm64) * > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - PLATFORM_DESKTOP_SDL (SDL backend): +* > Windows (Win32, Win64) +* > Linux (X11/Wayland desktop mode) +* > Others (not tested) * - PLATFORM_WEB: * > HTML5 (WebAssembly) * - PLATFORM_DRM: @@ -446,8 +450,8 @@ extern void UnloadFontDefault(void); // [Module: text] Unloads default font f extern int InitPlatform(void); // Initialize platform (graphics, inputs and more) extern void ClosePlatform(void); // Close platform -static void InitTimer(void); // Initialize timer (hi-resolution if available) -static void SetupFramebuffer(int width, int height); // Setup main framebuffer +static void InitTimer(void); // Initialize timer, hi-resolution if available (required by InitPlatform()) +static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform()) static void SetupViewport(int width, int height); // Set viewport for a provided width and height static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path @@ -2933,7 +2937,7 @@ void InitTimer(void) // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. // High resolutions can also prevent the CPU power management system from entering power-saving modes. // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif From 049a6d475ddb10b81ccd896c05c7c6b878d499d8 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:55:02 -0300 Subject: [PATCH 0130/1037] Fix drm hang up on exit and mouse input issues (#3484) --- src/platforms/rcore_drm.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 6f459b1af..82701e310 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -906,6 +906,12 @@ int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- + // Initialize timming system + //---------------------------------------------------------------------------- + // NOTE: timming system must be initialized before the input events system + InitTimer(); + //---------------------------------------------------------------------------- + // Initialize input events system //---------------------------------------------------------------------------- InitEvdevInput(); // Evdev inputs initialization @@ -913,11 +919,6 @@ int InitPlatform(void) InitKeyboard(); // Keyboard init (stdin) //---------------------------------------------------------------------------- - // Initialize timming system - //---------------------------------------------------------------------------- - InitTimer(); - //---------------------------------------------------------------------------- - // Initialize storage system //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); From fc7dcff4a72e3ce9ea970ad4e03cfabed026fbad Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 21:11:30 +0100 Subject: [PATCH 0131/1037] ADDED: Pseudo-random numbers generator! --- CMakeOptions.txt | 1 + src/config.h | 2 + src/external/rprand.h | 306 ++++++++++++++++++++++++++++++++++++++++++ src/raylib.h | 3 +- src/rcore.c | 41 ++++-- 5 files changed, 340 insertions(+), 13 deletions(-) create mode 100644 src/external/rprand.h diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 799530951..823128b0f 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -37,6 +37,7 @@ cmake_dependent_option(SUPPORT_MODULE_RAUDIO "Include module: raudio" ON CUSTOMI # rcore.c cmake_dependent_option(SUPPORT_CAMERA_SYSTEM "Provide camera module (rcamera.h) with multiple predefined cameras: free, 1st/3rd person, orbital" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_GESTURES_SYSTEM "Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_RPRAND_GENERATOR "Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_MOUSE_GESTURES "Mouse gestures are directly mapped like touches and processed by gestures system" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_DEFAULT_FONT "Default font is loaded on window initialization to be available for the user to render simple text. If enabled, uses external module functions to load default raylib font (module: text)" ON CUSTOMIZE_BUILD ON) diff --git a/src/config.h b/src/config.h index 96fdd065f..c6d553a5e 100644 --- a/src/config.h +++ b/src/config.h @@ -45,6 +45,8 @@ #define SUPPORT_CAMERA_SYSTEM 1 // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag #define SUPPORT_GESTURES_SYSTEM 1 +// Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64 +#define SUPPORT_RPRAND_GENERATOR 1 // Mouse gestures are directly mapped like touches and processed by gestures system #define SUPPORT_MOUSE_GESTURES 1 // Reconfigure standard input to receive key inputs, works with SSH connection. diff --git a/src/external/rprand.h b/src/external/rprand.h new file mode 100644 index 000000000..a9b3a6eaa --- /dev/null +++ b/src/external/rprand.h @@ -0,0 +1,306 @@ +/********************************************************************************************** +* +* rprand v1.0 - A simple and easy-to-use pseudo-random numbers generator (PRNG) +* +* FEATURES: +* - Pseudo-random values generation, 32 bits: [0..4294967295] +* - Sequence generation avoiding duplicate values +* - Using standard and proven prng algorithm (Xoshiro128**) +* - State initialized with a separate generator (SplitMix64) +* +* LIMITATIONS: +* - No negative numbers, up to the user to manage them +* +* POSSIBLE IMPROVEMENTS: +* - Support 64 bits generation +* +* ADDITIONAL NOTES: +* This library implements two pseudo-random number generation algorithms: +* +* - Xoshiro128** : https://prng.di.unimi.it/xoshiro128starstar.c +* - SplitMix64 : https://prng.di.unimi.it/splitmix64.c +* +* SplitMix64 is used to initialize the Xoshiro128** state, from a provided seed +* +* It's suggested to use SplitMix64 to initialize the state of the generators starting from +* a 64-bit seed, as research has shown that initialization must be performed with a generator +* radically different in nature from the one initialized to avoid correlation on similar seeds. +* +* CONFIGURATION: +* #define RPRAND_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* DEPENDENCIES: none +* +* VERSIONS HISTORY: +* 1.0 (01-Jun-2023) First version +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2023 Ramon Santamaria (@raysan5) +* +* 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. +* +**********************************************************************************************/ + +#ifndef RPRAND_H +#define RPRAND_H + +#define RPRAND_VERSION "1.0" + +// Function specifiers in case library is build/used as a shared library (Windows) +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +#if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #define RPRAND __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RPRAND __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #endif +#endif + +// Function specifiers definition +#ifndef RPRANDAPI + #define RPRANDAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Allow custom memory allocators +#ifndef RPRAND_CALLOC + #define RPRAND_CALLOC(ptr,sz) calloc(ptr,sz) +#endif +#ifndef RPRAND_FREE + #define RPRAND_FREE(ptr) free(ptr) +#endif + +// Simple log system to avoid RPNG_LOG() calls if required +// NOTE: Avoiding those calls, also avoids const strings memory usage +#define RPRAND_SHOW_LOG_INFO +#if defined(RPNG_SHOW_LOG_INFO) + #define RPRAND_LOG(...) printf(__VA_ARGS__) +#else + #define RPRAND_LOG(...) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +RPRANDAPI void rprand_set_seed(unsigned long long seed); // Set rprand_state for Xoshiro128**, seed is 64bit +RPRANDAPI unsigned int rprand_get_value(int min, int max); // Get random value within a range, min and max included + +RPRANDAPI unsigned int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates +RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo-random numbers sequence + +#ifdef __cplusplus +} +#endif + +#endif // RPRAND_H + +/*********************************************************************************** +* +* RPRAND IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RPRAND_IMPLEMENTATION) + +#include // Required for: calloc(), free() +#include // Required for data types: uint32_t, uint64_t + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static uint64_t rprand_seed = 0; // SplitMix64 actual seed +static uint32_t rprand_state[4] = { 0 }; // Xoshiro128** state, nitialized by SplitMix64 + +//---------------------------------------------------------------------------------- +// Module internal functions declaration +//---------------------------------------------------------------------------------- +static uint32_t rprand_xoshiro(void); // Xoshiro128** generator (uses global rprand_state) +static uint64_t rprand_splitmix64(void); // SplitMix64 generator (uses seed to generate rprand_state) + +//---------------------------------------------------------------------------------- +// Module functions definition +//---------------------------------------------------------------------------------- +// Set rprand_state for Xoshiro128** +// NOTE: We use a custom generation algorithm using SplitMix64 +void rprand_set_seed(unsigned long long seed) +{ + rprand_seed = (uint64_t)seed; // Set SplitMix64 seed for further use + + // To generate the Xoshiro128** state, we use SplitMix64 generator first + // We generate 4 pseudo-random 64bit numbers that we combine using their LSB|MSB + rprand_state[0] = (uint32_t)(rprand_splitmix64() & 0xffffffff); + rprand_state[1] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32); + rprand_state[2] = (uint32_t)(rprand_splitmix64() & 0xffffffff); + rprand_state[3] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32); +} + +// Get random value within a range, min and max included +unsigned int rprand_get_value(int min, int max) +{ + unsigned int value = rprand_xoshiro()%(max - min) + min; + + return value; +} + +// Load pseudo-random numbers sequence with no duplicates +unsigned int *rprand_load_sequence(unsigned int count, int min, int max) +{ + unsigned int *sequence = NULL; + + if (count > (max - min)) + { + RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n"); + //count = (max - min); + return sequence; + } + + sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); + + uint32_t value = 0; + int value_count = 0; + bool value_is_dup = false; + + for (int i = 0; value_count < count; i++) + { + value = rprand_xoshiro()%(max - min) + min; + value_is_dup = false; + + for (int j = 0; j < value_count; j++) + { + if (sequence[j] == value) + { + value_is_dup = true; + break; + } + } + + if (!value_is_dup) + { + sequence[value_count] = value; + value_count++; + } + } + + return sequence; +} + +// Unload pseudo-random numbers sequence +void rprand_unload_sequence(unsigned int *sequence) +{ + RPRAND_FREE(sequence); + sequence = NULL; +} + +//---------------------------------------------------------------------------------- +// Module internal functions definition +//---------------------------------------------------------------------------------- +static inline uint32_t rprand_rotate_left(const uint32_t x, int k) +{ + return (x << k) | (x >> (32 - k)); +} + +// Xoshiro128** generator info: +// +// Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) +// +// To the extent possible under law, the author has dedicated all copyright +// and related and neighboring rights to this software to the public domain +// worldwide. This software is distributed without any warranty. +// +// See . +// +// This is xoshiro128** 1.1, one of our 32-bit all-purpose, rock-solid +// generators. It has excellent speed, a state size (128 bits) that is +// large enough for mild parallelism, and it passes all tests we are aware +// of. +// +// Note that version 1.0 had mistakenly s[0] instead of s[1] as state +// word passed to the scrambler. +// +// For generating just single-precision (i.e., 32-bit) floating-point +// numbers, xoshiro128+ is even faster. +// +// The state must be seeded so that it is not everywhere zero. +// +uint32_t rprand_xoshiro(void) +{ + const uint32_t result = rprand_rotate_left(rprand_state[1]*5, 7)*9; + const uint32_t t = rprand_state[1] << 9; + + rprand_state[2] ^= rprand_state[0]; + rprand_state[3] ^= rprand_state[1]; + rprand_state[1] ^= rprand_state[2]; + rprand_state[0] ^= rprand_state[3]; + + rprand_state[2] ^= t; + + rprand_state[3] = rprand_rotate_left(rprand_state[3], 11); + + return result; +} + +// SplitMix64 generator info: +// +// Written in 2015 by Sebastiano Vigna (vigna@acm.org) +// +// To the extent possible under law, the author has dedicated all copyright +// and related and neighboring rights to this software to the public domain +// worldwide. This software is distributed without any warranty. +// +// See . +// +// +// This is a fixed-increment version of Java 8's SplittableRandom generator +// See http://dx.doi.org/10.1145/2714064.2660195 and +// http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html +// +// It is a very fast generator passing BigCrush, and it can be useful if +// for some reason you absolutely want 64 bits of state. +uint64_t rprand_splitmix64() +{ + uint64_t z = (rprand_seed += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30))*0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27))*0x94d049bb133111eb; + return z ^ (z >> 31); +} + +#endif // RPRAND_IMPLEMENTATION \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index d8bf1e07c..115e2be99 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1068,8 +1068,9 @@ RLAPI void PollInputEvents(void); // Register al RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) // Misc. functions -RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator +RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) + RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) diff --git a/src/rcore.c b/src/rcore.c index 1a680ee9a..902f84c65 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -131,6 +131,11 @@ #include "external/sdefl.h" // Deflate (RFC 1951) compressor #endif +#if defined(SUPPORT_RPRAND_GENERATOR) + #define RPRAND_IMPLEMENTATION + #include "external/rprand.h" +#endif + #if defined(__linux__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -1647,31 +1652,43 @@ void WaitTime(double seconds) // NOTE: Functions with a platform-specific implementation on rcore_.c //void OpenURL(const char *url) + +// Set the seed for the random number generator +void SetRandomSeed(unsigned int seed) +{ +#if defined(SUPPORT_RPRAND_GENERATOR) + rprand_set_seed(seed); +#else + srand(seed); +#endif +} + // Get a random value between min and max (both included) -// WARNING: Ranges higher than RAND_MAX will return invalid results -// More specifically, if (max - min) > INT_MAX there will be an overflow, -// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold int GetRandomValue(int min, int max) { + int value = 0; + if (min > max) { int tmp = max; max = min; min = tmp; } - + +#if defined(SUPPORT_RPRAND_GENERATOR) + value = rprand_get_value(min, max); +#else + // WARNING: Ranges higher than RAND_MAX will return invalid results + // More specifically, if (max - min) > INT_MAX there will be an overflow, + // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); } - - return (rand()%(abs(max - min) + 1) + min); -} - -// Set the seed for the random number generator -void SetRandomSeed(unsigned int seed) -{ - srand(seed); + + value = (rand()%(abs(max - min) + 1) + min); +#endif + return value; } // Takes a screenshot of current screen (saved a .png) From 15632876f7d27857888d5eeab5caf0e322a5b791 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 30 Oct 2023 08:02:35 -0300 Subject: [PATCH 0132/1037] Fix examples Makefile for SDL (#3486) --- examples/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/Makefile b/examples/Makefile index d10a35be4..5af5a5590 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -52,6 +52,9 @@ PROJECT_NAME ?= raylib_examples RAYLIB_VERSION ?= 5.0.0 RAYLIB_PATH ?= .. +# Define raylib source code path +RAYLIB_SRC_PATH ?= ../src + # Locations of raylib.h and libraylib.a/libraylib.so # NOTE: Those variables are only used for PLATFORM_OS: LINUX, BSD RAYLIB_INCLUDE_PATH ?= /usr/local/include From 9642fffbbbdda0d31bedae6126e7b3b9073ec407 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 13:13:31 +0100 Subject: [PATCH 0133/1037] REVIEWED: `GetRender*()` issue on macOS highDPI #3367 --- src/rcore.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 902f84c65..415fecfda 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -748,13 +748,27 @@ int GetScreenHeight(void) // Get current render width which is equal to screen width*dpi scale int GetRenderWidth(void) { - return CORE.Window.render.width; + int width = 0; +#if defined(__APPLE__) + Vector2 scale = GetWindowScaleDPI(); + width = (int)((float)CORE.Window.render.width*scale.x); +#else + width = CORE.Window.render.width; +#endif + return width; } // Get current screen height which is equal to screen height*dpi scale int GetRenderHeight(void) { - return CORE.Window.render.height; + int height = 0; +#if defined(__APPLE__) + Vector2 scale = GetWindowScaleDPI(); + height = (int)((float)CORE.Window.render.width*scale.y); +#else + height = CORE.Window.render.height; +#endif + return height; } // Enable waiting for events on EndDrawing(), no automatic event polling From abdebc244d41508c246a3a604f9f8b94d5758704 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 13:14:15 +0100 Subject: [PATCH 0134/1037] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 415fecfda..fdc521207 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -764,7 +764,7 @@ int GetRenderHeight(void) int height = 0; #if defined(__APPLE__) Vector2 scale = GetWindowScaleDPI(); - height = (int)((float)CORE.Window.render.width*scale.y); + height = (int)((float)CORE.Window.render.height*scale.y); #else height = CORE.Window.render.height; #endif From b8fce54c0fc443c76dcac7c7e1adba9fd7e2a4df Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 13:29:14 +0100 Subject: [PATCH 0135/1037] Minor tweaks --- src/raylib.h | 2 +- src/rcore.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 115e2be99..3f6328ada 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -506,7 +506,7 @@ typedef struct FilePathList { char **paths; // Filepaths entries } FilePathList; -// Automation event (opaque struct) +// Automation event typedef struct AutomationEvent { unsigned int frame; // Event frame unsigned int type; // Event type (AutomationEventType) diff --git a/src/rcore.c b/src/rcore.c index fdc521207..333fa4bfa 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2995,8 +2995,6 @@ void SetupViewport(int width, int height) // NOTE: We consider render size (scaled) and offset in case black bars are required and // render area does not match full display area (this situation is only applicable on fullscreen mode) #if defined(__APPLE__) - //float xScale = 1.0f, yScale = 1.0f; - //glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); Vector2 scale = GetWindowScaleDPI(); rlViewport(CORE.Window.renderOffset.x/2*scale.x, CORE.Window.renderOffset.y/2*scale.y, (CORE.Window.render.width)*scale.x, (CORE.Window.render.height)*scale.y); #else From 7677e4b92842a317f883c91ce9ad0cd6963d9341 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 20:41:33 +0100 Subject: [PATCH 0136/1037] REVIEWED: `GetModelBoundingBox()` #3485 --- src/rmodels.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rmodels.c b/src/rmodels.c index f9018eaaf..0a997f858 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1170,6 +1170,12 @@ BoundingBox GetModelBoundingBox(Model model) bounds.max = temp; } } + + // Apply model.transform to bounding box + // WARNING: Current BoundingBox structure design does not support rotation transformations, + // in those cases is up to the user to calculate the proper box bounds (8 vertices transformed) + bounds.min = Vector3Transform(bounds.min, model.transform); + bounds.max = Vector3Transform(bounds.max, model.transform); return bounds; } From ff04d52f12c95b0b25faaffc4e68abed9ba2b474 Mon Sep 17 00:00:00 2001 From: Jett <30197659+JettMonstersGoBoom@users.noreply.github.com> Date: Tue, 31 Oct 2023 03:43:32 -0400 Subject: [PATCH 0137/1037] Added rlEnablePointMode (#3490) for rendering meshes with points. similar to wire mode. (NOTE) they still backface cull, so disable that if you want to show the entire mesh. --- src/rlgl.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 7c93bf929..707555dd5 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -632,7 +632,8 @@ RLAPI void rlEnableScissorTest(void); // Enable scissor test RLAPI void rlDisableScissorTest(void); // Disable scissor test RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test RLAPI void rlEnableWireMode(void); // Enable wire mode -RLAPI void rlDisableWireMode(void); // Disable wire mode +RLAPI void rlEnablePointMode(void); // Enable point mode +RLAPI void rlDisableWireMode(void); // Disable wire mode ( and point ) maybe rename RLAPI void rlSetLineWidth(float width); // Set the line drawing width RLAPI float rlGetLineWidth(void); // Get the line drawing width RLAPI void rlEnableSmoothLines(void); // Enable line aliasing @@ -1817,6 +1818,14 @@ void rlEnableWireMode(void) #endif } +void rlEnablePointMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + glEnable(GL_PROGRAM_POINT_SIZE); +#endif +} // Disable wire mode void rlDisableWireMode(void) { From d8acceca14da2ee5f8e03cd23347b28e438334bd Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 31 Oct 2023 06:10:43 -0300 Subject: [PATCH 0138/1037] Fix example core_3d_camera_free (#3488) --- examples/core/core_3d_camera_free.c | 10 ++++------ examples/core/core_3d_camera_free.png | Bin 25317 -> 25956 bytes 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index 78200a642..ec849758a 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -65,15 +65,13 @@ int main(void) EndMode3D(); - DrawRectangle( 10, 10, 320, 133, Fade(SKYBLUE, 0.5f)); - DrawRectangleLines( 10, 10, 320, 133, BLUE); + DrawRectangle( 10, 10, 320, 93, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines( 10, 10, 320, 93, BLUE); DrawText("Free camera default controls:", 20, 20, 10, BLACK); DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, DARKGRAY); DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, DARKGRAY); - DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, DARKGRAY); - //DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, DARKGRAY); - DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, DARKGRAY); + DrawText("- Z to zoom to (0, 0, 0)", 40, 80, 10, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -85,4 +83,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/core/core_3d_camera_free.png b/examples/core/core_3d_camera_free.png index 7874eedcfd3a820bb10af294058e688e313481a0..71dfc1c80a307772d2c1a30c7039a428e4450308 100644 GIT binary patch literal 25956 zcmdSBdpy(q|38jtTiRxFXa{2~rBHKb$e2cymL$nlbBH-6Ih2&c*eG*~(m`1xR7y3{ zL77QP2Sbu{Sg9yPsZ^@pb5`%`dVj9#{rlW*zu)J3`+a`@?5eKU>+yU%ABX$n@O(U9 z*$hutqzX<&K|ukz%+1A1K>zA@Dm4DJT77AAk!ph%s>7pCS^9UV8IqOJWdk}p7ResG>bo~eZ01IPs7QFvov7j*PtksqaQ&`@< z-+UJ6jn#2BS-f(kF)fto|N5!YP?~%H#M{}nu8>{7|J@s|eCOv9+>8IB2yaB1#K2c@ zGwkTxzr)xla+hBJ#hy_?N#D@+B7kLA&6oDQ9LJG}vNI-alw=TALjv%5*!T zAUo=RHw)B{D7SnZ9%5yWtIR?xWUAJPArDbWy|53emcz#?cer-F{09qE*hx#x)at3Z z{4v5pX8~Ms0y@dsmOHQVw+8=e^3vH##uF3p`hqCNi-X=<;;dJ<`zKve9f0|NN*m~~ zBTV*lPHB~+)^leZ*IgURIk?O9!k)rOgPx~?rJet*kY@#- zX59NH;?GcNPUn?ptz{qV=2*qX>=o?Sa42d(zVdYb!Lhtr9q@p0Cufo(6wJ8&Pj7dq zDDD$I_Yb5$y}8BctT}>w{y{-QN~ZS*$#?4RYYXZk3`0C8#?s>H)QL-yM!MkT$ls_S zE}}6O1hl`Pza@8<`pKgVL;`&I%V_ka5qZ%^iYQa{6%1 zJh>(S%j5jJ1{D`(HVrP#5G?!e94R9^DsH=kHS-@G#-GL%Ld*`<{}+G$)3XP)lmx)t zTCDybLs^;x)yBg8Gt|m3c$XfT0$4cl|9cj+2>DAl1^7Y``)z7y`}22Hy99&g85 zLSD1zgui<3Z(9Jui=+_CpvI@p<}GI*L#%&+UFQ%>!q3aqxj)%*bD7p)^s&rFvK^DR z!(U2m``%V+m>jTdSqc9J_T0J#%KY+HqgX%8$XJfn>%ZXZuh2=>BM;gUdvR|X(pA=1 zDjna~{ZI{TBK*#|XJ%aQEy*{bhjiWI9b;d!8NScA+FeCKFX`4EU>gw_MjKpbmHp=N zFMKP0&1fonmzq!UT2WIF<&S)_)aKJ6hS4Q0aG9~_k2d@c!g{HwQgtE5Txz+Jok5i(^cqwv^y+6#OCG>HHD zClIc}{w?DE+XIsT3J`6q6aO)(DO(SpPFWuP4b=XX-hhM$`LDnH*Q^I|4UH)UG5P-A zjpWx(<3PRv{5%l<4}z2XKFD{D{}$!n(Lg#!NnlPCR44tPjLE*YQ;iqS664GpTEYmt zjDDI)qSPEVmy1+AK6m+^ZFrR*Iwo#^$Dz4gh4z?n`zk5+L7mEIY1*6QGVc6Kmj|&= z+N6r-5^FPN)`*$qUv~%%b^o?|K3Yk99HJB+*NpqtW^tNheJ;t@h5we8OD##7a9g^0 zcs71$uv9VmRnS|T{D{T;v8Gp!{-0LUwAXGE-R6ARiwmHh*x|aT_P3q=WdX%ET$wX5 zBdT7gKqE_4O8RVOhq}gCj=iX|Xism~_PRc9Ff-X)r~GRk=3`qlzgxF=m}E7)Yhjm$P-z~`J zVG9qBWm+yu>7tC%_q6>Jf6X$Nbt}=M4!hZ!G^5tOIB@u`vh66z;)8;jdQZM?Ta3Q- zuxluVzIEzvc8Q3D=ait@^k;kCYPR;ujcCzzz!lstFnjMgoTKy}p=@b4zjt_}OY4H5 zmamYt?_GQHhS$2EZyz*nV-?u_)%$+O(VUIfc70!&qyF1SxVK6P@O`wf_WdB$Bc( zM_@gZcTS9Z+9r5ED5XYLLd#JPmMd02?|q)`e<3&I*>5}gi)+eoE@wJ=4)V4I?nA11 zu~!@^DcBWf*_Q18bv#>4ai0t!wZ*?>CH&(a0Vxz5t4FOePm7$}_-Rf@`w@w;`QOZX znq{$xGUv_$UqNY}gVH_h(^JWb+26bwcFyFvV!)cp5#|Nnv<<9~n!V?hDsuX5^!r>(B^46tmy zeLK=3^{?57f{wiQ`qL$;32JAQX+T1E>(7*?39guwh@#t(1lcja zmFPL0SFZl{FC$d2QHFZVX9~^D4ibN}vW+|4Ps{w>kb$GpL{EBkM{;sP?)`RgP{_!d zKL1KEo1GONZMn$)UghsMLc^|@`jh<$)ig=aHrLgUf31o5fF>RvUxEq6ja>|j3kkM( z^t&4905A&LaNWQ20Wn$GFsw{9_{9>$d&c2FFLfjp5%U_oB;|(zHY7ne1n+ z>Uu0FEszY`|I!)2Qd#q;I_~$oG4PI)q7_j)2Bo8tyKStV-$}R#JFUH<-pKLZ`d< z;Tr?#{aK^0_|K*(fBHpP@vB_$_kZh@I8!l_C*^li>521m95pDOy_A)->yHHo5`S+d za9R*uzndQjCOp4Wgo!Lw3+y*9v13g3TT#r-{EUAU3JN3g!sXJhY9#fp9+lTRLcL4Q zkNC`Fb?<$_k%>gj^w-Y1WpS;IwHpWi3!1NrlnjL$+}Vk6?bVNTlC0iThdlr$C~Wvu z9Vp(CHz#)grViCh?PtuuuFP|oLAI;jG^f$dx*J6b=faZAu)vtK2)~SkxH_&hJf(%T3SZ_uEcz#3#;t)AI^!iE4R6Sgri=^52jD zm~P!3&;Mn)L{nu>AVmG~9okLR-hPUAzawxrlMTOkRxp=aOW-d7XTj6>{i)pTst;Ak zXZZkX$N$o~mXNIBob~O+pk%3-7f%p$C<=Oo10-TBQZ<*rcn|Gbw zJc**rl58VnK}{`3w5!1H6d6My4O|L{^9dG`<%YdL{=!@N3yYKoA9e^f1s&=6QMT4b zvM>j#f5hoPHqK*Qh7&VBO!E_inpXQ)IRQ02atD$U{?L)}qpZGh;uEaK&Q{p}(_FTj z@@dH6$g9<^iRrg%k!hm77*d_7eeaZ0bkHvHBpVlvaZAYoVw*KQEjB57+JoZcK6=WT zX=vFoW;wu3cg))Ig82zn9ZkCMV(CeQm28bG@-@Qc5*wXfBOj$WK*zL^Xd@eD z>?Z}d-K9?XDV(IBT{26nng%|^C%NHBrdifgQ!2MO0o_45g1Lq$&V?_zhN!!%VRa30 zAElQRiY~gz4d=!Pv*aTCH*jQIDH{@Q5|)!h1D7_Db3^vNIW8K64Y;gaK0m1To8l|o zyD%n&@_nYreL>E7nTn#HWg#?h?xDV2Sh!<;A7g$|3DkpB0(|3@r{3A~+A^$5 zPnot>qTKy{Oq&YSLOCDrOyqVX9e~&1$0u05lJnzk9+#o#R=d^2S>+?Qhd*=x;;oUN z3JemaHDWQH5l}y)F$knixYaHgjZ}1yU>*B?tJjU0hxgq+GfY~rLPD7u)O4Jv=W3OH zl)**jbiAYH$;EYECN9NG8BNTn-uy>u*vKDzA#G_`C5;WS#6F%BHBrM|m4lT`C>+Sy zF9#3)NX;}hx!tn=i%eg|Vwt`aYUC(YKfMu_mJ+R6k+%FrwBaPz+q$r;D)UZUSt6y@ zU@pT|d9LS~bp@h6Q{_xWTLNG7{HTnfrPKIDy(4F6x|6PWg44v%yW$P zPRlh=u4A8R(PXJ8#^UF&FOr;39ObyXW?&KnSJU=9L?cd<7n)%u$HF?#Muz z`_`_$Ofu`>;rw`4_C|~{M&p?62WgvpGIY$z5Yu+r$uzdiR@MLvA{oi!v@CABfncI? zq>yumIf1vtaRA9A{P&irV=(NwC1{@(=)Tiu6$C6r&pLKl)L3)j8@F|VOT&7~@6R@geGx_6gA07zZ|SlK zJuSY+Kzu^E_|gH@ERMoem2HMr`S}9HLr*VpdhfhxDAYXnlo_5iz%4qzr;IF1F@1&rM;3Af@B$RkTm{R8tCM+;(mqiP ze&B}*YOzz$t`CmY=>fGHXWMkoLOB!ElD~wIUoQxJzNum4jPvIYF{2i_KD~gBznsoK zKqo&_p@u#N^$=wD{lG^8D&p;N+4q|^x%h@=bPH{}>5sUNW^Qeu==6r{Z4Ow~# zdXr!)G@e5ZQSE+mQ05!v0T#+ty3--W+Y+bb4NsH$I0c*>=CXBXT6P*paN9+s~EP-q?E>1wRWjKzwvq*9$e{ z1O*Wu9+hPiuR!o4%KZjMkA!y!%$j^|cG@+C4c!wxa~v2euipW;gl>ta((Uv~0$)9g z7fan?8Ql*r@cT*(tnyIYR?1bUgVJZ$B(Fe2k1>1N3MR~tIWx;tb|zVO4~^VQ8wOr< zrUA#zpb!Ow0_nOd=*!)?B`CP1)qd5?R%3%rEsTg$d*3{e*%B>sE;xw=#~qJVzo9Y9 zGwrUbnYxn#$&-6G5DL=X8zlu5^rG5WuEEq~{uTs0H*~1t& zs>8w9)dgus))_Wt!~o@O+oL<7UgH+OAQq-aDlEgb*X7kIIxv`mklBW#>e4UsMEn+-<#cciOYGn_xh-H8e4n|e zh`+1H@$@R+l53o#ey__AvpzL`$`+TCo==(YQk$F)7TVhsLEWmPJ)+U4G7Yk$4dr2> z5p@Yo-JB(`_Tfedv~Be{0^Ne|)PMBMzCTfGQaVpgZ|77= zdOU@CJb!DXnQi^v*jZnhr#W=@7n+Zc(i(@a7ax~piM*>Kxp52K9-zxYq}JXG;o+0p z4dWXO$KNsD&o_RNpM=%3H0vJ>iGS|Ag(wcQ0mByUz0y8ISI-q~FXy-=+brhFqOg?$ z%uJpD<-O9QzIBc!Tls}aUyC+;S>3*qR$TyzJ(=WlJ+)XLY;D-d5!1Y>Dv>%?d{SH| z-F@dCt=e9{l9>Kwdqe9GOMUa0e0%YobCn1GC_!>O;f+)FB@}<*#0O^0q~yCj+MGCe zAVEk{npq&yKv`GDlM&a|;=DdQj$xIlzu-!Hs5f4}mWfK7uOqkZQ16+F14Od z!C@!LblTjHKG~&XjMVlY#UKc1-+4OKgaXmv=3Yb_Bdl!oo4fp8mcfI2w8k`4tm9da z_Gxjs_{psxs(_%xu4C#~#2DQ8z`prZ_{qpP8OlC7^m6Thg;UNo*lJkt+;|sgV36&C zmovW3J|*(s;I*TJWoY47z2*pF#!VBL2SqWX}(%6qRuXJ(ZKm7M4m0g*C4 zS=W8vU8A_f*DA2H^UT249pnTmXRB*))$RAs9zmSX#O0ic(aqVOt#g0eZQ)``yG8iE z7c%T?CH>C%m1#`FITl4Zu}I65ymB=MKMVaf-aAphBG!{oz!zT0>9wOmkCDt6rAN~i ztsEM9C-P405I>a>;moy_i_O}X#W&@qK`3<&6fbGr*;w^zN~8vg1Y|5>76v1=_Qy4EsErrtm1OiahdT8p$AH0IO{ ztvW$SxmIf151pBW6%(zn8a#=15n|>`(Pw{mG4g8H509fV6AD*HBR`sz_S<{Bz2bTD zm6Bz@L($X{ueZ-7i{SE#pSqhk@4V^*Pxzv1s`q#AE*jW9`dNy*(c7GHM6*9;FcsnL z8f)4dRg)J`XTxIw}>4!vYTQ1b-5>> zW>??f9C`}UJJQKL7NGx0A78<{?eVr1SCf8d_BGWtj(g3_cI%9aMfb>#$hf5r+IB?Y zn=HiVT$pkH*keWKJ7zI+t$OpnBUIW8&Lyhti)Q{Hcy6+(w>O>LWhI6~0BGxJON9%w zxb7nQ9VL~J9>$5=h97@2Cd^|K3~qcEj)B}Zm2r|}=0(w&c$3QXxIuGp-0P(&ep6L2 z5b6c>=d8C&04b#Xj(IX#T4yG1WdUEStos?D`G~nfOE~tE`PsbQ6Xupvcvru!>ocPv zCm$DfusgTK?d`UXOAzPfmk4)xmi|9<@9t`~9yIfP8|HWaXxsSmFhUbq1!^yYDH>4? z6)dG-aFr9EeljESyTm`)jrxM(Z&jKPNEHwzqebKK#+*mlJ_Fur-5b)EMt{{^g!J~B zGWEA3P2M^oeG@uych1+cF1NAIF{AT&2~=pGLJp!=YFf4KfKv@TfrNywAXO{JS4(3 z#T>C!)|_@xvoA46JK&D9B|j&k^Hn)y%%J;@vf;{>Ck1_qPNP|@7;=shNKyI}`D=7A z?mUZdj))O;*vs!H;v?PmVi>dP+m3RLav(^>+#$Y?9%IMePmD3gied%+Tq|F3YR?Zk zYQIPWI^dloAfTZfa0aNqbakg9f9+M$g)Odc7w+vD#hv=m>Z9Xz{whJ`2VE5sJ}K*E zZo6y_UZC7S#)c@YbLh@Rm(yR`Xv|U$pYw!ffS|_)r4|X7fE|paHl3IqyI2qO_MS7t zOS}ZT^y=MpPi-RCh?$1n4nh?+zxNa7?s+{KwU*HskBI>dl-K}aXo7*Q-n7!av-0?5 zu9qJX3;=>N1Qh%tAz$P*4-tG-dytGlY$2ngF*%z|E8>S4?^4Zjh(KukRly*ZTAzNV zcf3~nbO1G6(e`k2TK}zpDy|}bLe5h@;0(dr;C?GH+`E)*f`Fbltbf73BILuSvi8BW z<+H?fHD`NulBs%u56R5 zUj`G`<0)3Gf>a9(Tnq@$FJ-e0v7e&77@|9eOEqWhAe+F&8fnJ1~}S zZrpZ+Uw^Ng~+ZuyDajHtI228>oH}Shq&&C z(t5@%OT+=?_lG7%%fO6H73Mjuf*Md3SUKxqRYCVS_0wm@W?x_u?0AZ+pAGR_BAB=9 zQxx4-#72Kf_y39D#+1?7aaA0M3GM~0`i?~(mcQ$Bun^+>l1vyaMJFGKY4}E1Ul~+x zbu}Qs)X&hIoZ9>>#p`<35>7(gOs!*asdrKl7jj`qYnHh`(anvA*pA!zH>Y*Rrc}M= zNs{s?$k(!FB`kvp&zacGDQf-XHk1)?lFHe^p0%D14iAXiKb}(I6u^NaX8B&Tt%2Oe zhIy=VIzs>a!Oc)Tt45a_@t7JOl-!Uhc02XCYRwJcV5|;53<{gm?L5O4!(FXN^Rxl9 zLmIy7I`Bx=4f*?-X}B^{4GC;Ctvt7&_e;~h#mqob)Vf9Wt%GjiWwYPEjX8}s3^O;) zVN4rIi|X|p`W=Kt_bi;d5l`wpl>cJCbRZ>}pjx8+RVTloXC|;+GP&uwcHFG5ZqYf! zZ-hsu+T2(XyopvrtDmZ`@4}J}>Wt)|_qV&+5iN|7TxX786CE4QWB6rUXPCrQYif!4 zoqATrwU1{2tkWl5grw`-C_}+bOM9d}=o@REsMaN=;>Od5L;ZTr()5tLuRk^H57OT_ zIW%+*0qM2cZX;VY4gNtyyrLUAGzXHu={{&fTF$z~?^Dpb|9El{Nqdw~P|&L=(ySZ= z8H^78G;&MecS|qavB_KC3VN_K)jb7;&{|6)u9scv2ErrYJ_P3^^4u$FcPH5kJ|JCu z5wCW0H%Lvr7jBGwyzF+=ptz1?R!AXcJV&pE1=}RJw3$5M2GI^{IPx+$HhiJ!>}Cbq zdwF+vF|F#srsLkcDOk@~{rrk(p9rRp$QN%WdnZ8%P_~{ubTy{TTE!_j)<`9gLL`3R z2F(wi8%NE`w(eEC(o*z@c2UjKQe36l7wzVykYO+5wT5l+i_KNuV;Nj)y7w;)@Vxcl&})MW=UDs7y64 zzn70h8#YVB4H)M3oNx^D{$G9Z5650 zJ?JD8m6x`#EKKc3IY_itgNU3!Z5tvMb^*Fsh@RFtjqevawA0;Br1)`eFlIsq#smr5 z5OoR2BtP6pGWzZxGK?KG@FE4|hIn>o;ZI0S{j!~+XIHuk_Vk2h^rPBh2D1>AD7b<7 z>LS_1@>X^Q$h~+{Q6Idr@-oN0k=~w!C{pG4efNAj#>C8%&F4|omU$`>-jt#uNy3 zI4_wdkA?{I*p0dPHVd=vuLd$kb4&wgzhOi(X5)qBA-z(}yds}Z?25D0u!LjB9R~^X z2KTFZ&DNp;f9kd)cB^a-PozRmkj#AXs~~IcBJ1DBlN&<2ktGIGtk-Oz-)6vS(;xK6 znOghKE+{2n+#3U&v5DmwGZ)P0jX|l$&U&AQP!9(R+b(MtbHZU6eNvB;D0m}9zjn&J z*Se+4{HW?H`b%nYBHLs+l=g>B-xp%zbz1;~$~AEVx6KPFgh8jgBWW-i-wb_|&2X~v z#?36^5ElHfvh(p-i36~sy2GRNo zmTrUbL@K-EmD`2_$(&iBtg9Wv=YS&P2?(zxlFN|v!mX4;s#&-Ol8NaHBW>5%R=Vnbi8HEjS@oI&oeSH$K$VBF(`VN~oZsE; zqXOX$r88&|HWu@;?}*DM;~TiMF8;tre^jk|Qy@t?@cv*NAlF9k_8 zi19Cp<(V`11&>7~D+eDd9>Bg*M9qW2V9<}Jb}O3Pgsz7LmxW|QX)xuruE9b?0JF*R zylEkBZ+VYMynVaPMNlc+g~gvZ1pHO}3?eq&vfAql^7U=ca77z%29V5yGDZo;nsXZ| z#M)ffuO|oy_9m;~T97moR3igi@(Op6sS|F(UOYR&1qjm}Ma}~m+c>L(v3$6DIIhg~ z4&&ZbH|mho8{^f7!5H&q`o&S(!d$vQ`<+2R2Yz3cmkY0Xr=VAHqIthAYaQJjp_r;; zB`akK1(LnCdQ!8_53@|pPkJX0Dm$UdQlIt5S!D1wh5+I`LN{}u=uHf#GQ^HT04{N`jKVm_emnj0ubYKIoW@+m z>|7ydlEKL+3z)(roiJwHim})j`bAm6noYdMn#eaEFi>>Nqv*#%3agD7^Jf z3}hAMh`7M9t_Xu^>4@Wfg2_D<2r&YHG{-li(`YrKxOr3A!c39&5;p&scBGVchOAc) zF=O0l(Vvsp9ogG;40r%-QNIm~8^iN7g{w=W=l0~uMYrkj*`lcA#P$DPT)Ooh^Ju(e9j0IZ;I3p1xfW^o>_q(#b_va3d53tn&Ip(9j;H7aA;PVkbr586QXtW;mQ=SHBuLn1)0N>I?CDZe zc!D!$D`wTnRKlrZW7~BH`gVa{EywQ$yE7MV3o`8|aNu}_38xaFc$%44b5>z`f(=)#ZeiK!(~DweSKME9 zX~Oul+Ym7ytR7yfIuLW?{>AbTSg@g0*5DsY5XEF%TkKXcSHi4b2^psP=nW|=d%ZXc=w(5HleJ2AEK_kp5Hgqr#0<8GtC^XJ#6Z?o}!-|bdEsq(Zy zo8IE%UJFkA7J&oOrd7^^8mIXF{mApyo!KCU7!?I}Gv_C69N# z6m^i?i!Z)NJN(n>ha={-D77DJP@jyM`O>Q5<*gW#{Z%7pq=oxfe{9UFFNV6l1(ch~ zSDIL@&b*ZaO1mr~Yb)dZ=6s^ra`h^)AZrbFEKI_GWgv7Xa%UvLg3IIVnOnHYF!`=tqcq)}Y<4s(X^9qNP z=>c<`j&YZCITPUz{;tFGdZm7?|G03a|NYfSEeynatu((=4=6wX%j ztD8|n_8*s3BN)%a4S6lRK~U~tFt^VP2Q~S$mQ|{Ibz;SEB|#~=xnM=jrOh_;j3NWf z`@UfMO&tKsiZiBSZf0cADQ(&AekHb;LXEm9%h@V1?zoJG=Fo-&=loEI{4U%2)wMCz zgxsZz>al3&?V+H#xolhGAp#xZz;Mg;4O1;MXo2< z;Fgr|8l+xFRX@K1)kd%~4}+*!Zi~)*!qsrr5+)P%|V`)2t0(vqn3iwtWt`4M@YF3+UOU&n=N>^NZlN#l$JyMRm z=LtWx1@@j`Vy*ZhVlJ}cA==AsRtvDG1rb%c_dV{$8hM>KAbq077m`dnDf$Bm-OzJ7 zgsPDHtk*#-OBOIBS<0%KCi3AUJ~>C0i6aax6`&_fxr>p=R}ay#p$puGQd3n9^C@`e zk#(xcneSqTvl!Re#jyX58%? zl-vQeJh=A@4tmRQd9-SxHUc>;(%HbSQ8&ZjBPN?!Wp?`A2eXM6AZERG`oay-OWmVP zz3ucTj7c0C?a)5yj9I#jOZY{|Pa+=}uRbjsOc`SH)eid&7H{Fk@~!(;kq$dSX%&vW z_)tKNNYU%wptLj$rhE}3;vuxjdu31Fn)>1{&_ErLFSV3WKDC){7a{l~Gi`eO3BHm& zfr-MtA(>1ptm1wh)Nn9b>03_-7c{f^BqC(mPuV*=Vt~Ea<4yy; zg51$Y+3-$>xLR$+8PB#Dq`BYM2%Azw#^S?O7mfGmAh&~DY!G10@^AKSHM!7s{wx|_ zN-4qhvg`C5`U3D#m`;-Xj-@j=eK4mev&{coPjYp{Gn|sC*NhYsv7rKI1A>t@^wkJQzEJ{Wyt zKsR!_s4Aesp*1?$pg&@-URhFdhy)n{wUO3P zsF#>&b1wnyo;TU4!3U{V^poRjVai&&iu_Bm`o|RxSRck)32%9b&-x3vM>#YWIp3AT>1rd^3GnMhS=tGL{Ukl-!wdu9{yJ?#vp3Gjli6pqW@REQ z<8W?U%z9v!>*#l`SoY)D!f>YQg&4tF2kZW>M{d#SawcuSb?F!C`)z<}wS?+NhJ_1X zq&ZBDy>w)_W^CC2^L@c=unsDt5Zef9{!%hnW^lot&MQ`hZ?Wnh?Ok`@~b)A|B zqD1Nvbqj6J3&++BgR^9QNKkTBaZu6Z&hkvW<#)TjPnaLPE?EI$NYqdg%uo!0OW(xh za5SY;$`MmGHa6s(MXN}sr_w5nB8MV~(O#~JADM%xP5sJmvxt>71wH7w^**HIBs0I` zPR@I~Ei4Mf$hF_yA}?vL*a}VrrddvrUB1s+sodi2EXbVyX{@EE7aN6{z5i2Ibw^ly znx5$*ZDVkpPXn->4Zxvj&AM_lgIVD8b9ZL_dj1sC) zGZc)6>7PRj_ANB5F{1n7v_~3v`zU4dNdO2qcbaQ)B0FqK4bLK-Rd>F*%&r^pjbBo? z?4#q7j}yZg2p3>0T{ZNiug&}C*A}l4%r+j>r6XueU;3cuRrb1)oG{DAPPZG9aEU0RI~pl;zs(ueaO894AWKT`RVtJ{SuuA zC7`Q0UN-k#?nJ9<^$*>VtTLbd8O@u8r1WdsO+f&Di0qJeB8S23N9GOc8icr_sbVcH zxWVU{aAM9?3;Ib=*x3;Wj4$ZdGx6Cx7x(Y>ouGi?=q`6~fL`Lb?+t0_&J?V0doMq< zO)^wTYD!D5=8H`4i(?+Xj$%4{ks3%xx%(VZNBxk_)epT7%r+ zH0Qe8=bx;->T1(R+TBzzXZ5L7%GBP83rD^Yb#!2EmFB0Nh#SwuT87f99xL*%#~KCB zx+LwR9;>)U2V-#uH+8s0W~sh3OBPnUeV40k;R!IHXzUB!q3Ecg@wE!4e#E!Tdsdprs|it| zw6j+07Q0c;RAb?J(LU7pMQ*1j+>%0^@Q=1E452~w&wvyd-uQkzW;81&VF?|7uvc!= zi>h!+kaZo1`%1}V&AYT}DcRSkPILq{o{0zLh&zP{x!4~L7=Y3!+Hb(XgJBS7J?JNV zFzst=(VP~E(CH((EHn*bbjs-n=GFz4YFvf6H(rHZeNM+62}_PPs8n~3h6UFeUjP|M zfv9H?zx!o7l`D;@dIUnd0J%I_z877KnTmdM>}l0!UELMB&Np!^Rj8wa)|Y9J1I5sD z$MCnp)HXI0bgJork`%LlJ+dd+{lsPH2j*>A|EQkxfngjZqsQ}bmeV8APC(530dN*j z-=^2RRA`nUa{OK{fod?uBix&LG?_4_h@e zH)F!BEw6#1KS?3dc3#w6@TIt?bYoKa)}*!BY<@EC1n-r|XC6bgR_+~ROb6CQaW)-Ey_!(XMA&ew~cZB1G&&>jWZ870FqHya09T+`r7hM z?3jkiWl6;w#b?#686OAT7R`4(l>a;30H4gL)$|gdon5vxb+)>vfS}s1w=#T&?ZHKA z0ojX(nWbo7Q04%O$O5No9(#qx_vp^-0~>}m$qXxNuJtsCgmF*nDHxril?-s@&rq!vV_-{iy`C4@Tmxdu=UM+}ykFyG0MIx}1y;3s}}}CQE7iQp@Mc za|`aM9@&qzN^1c0@J6=f>*O{pd?Cq&jhEWwyC?78mCG?0t({vpj7NI7_<9Mh;BUsO z)`9ES4omvBm4lf~kiv$ssK8yu^c{PhG|#fx5HaKGmz`zj3C7=fR%2~@W=4KVv@p(| zd}+kg+i57ICPtRGbHd(wyv zFn*S`n??R*Nvy?G?ff7w`k<>gzu}m|MjJq76K{!E=UdkOOb6vQ6Pl|SAZITWLl1pP zUk$I5GjJTb0-iI_I?Hn59ZBn~aIbmu9l(pD&r^GAu}IqUJ>LFdaTbW1C->!xql6^q z?e~`)M7iR#gz|aSuQ+c#awkPkST)!6tDyxTQlP_W%T&53wZ(A+rM5{viyAr~v0O8z zd@*;#0%VIUjNmY)dSovZd%v~2NoSvz(QiN+r4F6@35w8MHeY0^jo&FCQ2I2|CWbqB z<&Ew_l?Me+z^HF>jumw1_W=GNSXCp3-Kc~=!Fw$l9F?F5@w<5v)MJ)S;cTUewnWld za8CCH69q@POfS^+I3J!S@~5KeR3fUpt=NrQC`CpiqF0}!oeDTs3gQNeP?e5@r|^n$ z+hmdfokqDG@1V_7hOT$AEi$SSZ5YZ)C}iz5fo+AvS6Lr{tv*Nqb1rG92hx5yga4XP z+U%Cu#55#xJu)pYMo0O5F*p?hg+bR<*)apmv9rjP5BX1;w3m2`9Ek)N)1`XJ8!KI=j9|{IA)gn=~`! z!F9IC&vi|ZbrRJJw_?a<xfen4{guVyuC$qGJ&yo~9Sc4QwQ9z)18%EU_HDs&}-3 zs|2&U3gU1ojYh|%J>%4SE`n0Z8%-m&NewvR?`PiKK{JhX`q)B!^1&t26E9Q5C+M%s zECb+;15|!V(R&MN0}t zcVnY^kiVd&TTfT1yQ+cy{NcL>{Uch)G}-C0>e?iWQ*`%9RFNY$;zo3B`621-tL(En z*wbN&!Xc{(xu|x##({bS6bfIf*M#u~a$&J;ohQ`DUgG^5e&~$!O6rp^r=@njZ;E9C zhHNIw6Ud#YJ!oQG8DV4_Iyb?6;TeFiA(LKq%jx(z2!s&9F2G!!xP5`7mkc|11Z$#8 z$0xEK`NG?2Z}KpvpN?x@&;N> z9p3P#195bfJb2ciE~BZ7QeEwIy0bSV7x%HmC0EGv3_!-O*^+5`F>H$k@&#&Rc;+1z zhSXf*^RbgI%8ZmyG!p|bxD}BeBxG=(8;v#BK}k+YytADgpQB0 zOC)x=iZMGdtIw0naIFH~MZWwCITm)2K-kpdySkex1ma%Td&QL-CUX-;S^Is)E4n0R z=@T*YEpOAsB{7)QeQH?b3kj#6A;-T<1OQ5Yfqwcigp3Afv-IcMiX}WApDihb2ykC=90rPdifBx-X@P@^l?vx1f?^w z`z!d3YS!8a62lx!)D`4Md9;(Nfvbu$thTIL@n-NPtD*2{=2#}7dYtT6Bt9cxY|T1xphe`CP+xa( z2zYxjVj1$JBp3cg^A78hc2pJehFm~z)$mHBvy94660>~mK_TL;1UfVP?9w~6I#Gt| zr)L{3OA&Xtg`I8Dfr_PF3OPByw4{LSU&eaLUr*g zde@>YFPm97?Wi*3YxzR&)O3}^7xTen(|5NMF0ke^q`CsRDs!jv!E=A* z%6S|Ga1n*ZcPue_FZHcP9Us$Kz-vfzcLl30zc;26wauKI!x6Fe2LH-lYrq>`aK!!Q zlq!o{&T3M0?eSu1UnUKqR0oK=RH=EN-lVEuEoh*lX&{z$5?qJFuc|o|ud&<1K>nmfqwYV`E8^r(*o|P|S1eOT~ zSC5}#ZVG}R>umGNV|17E%B1~7{Ow(MPbqkFI;=;7SANy-;1Y!!T+z>mNUB!MS==^6 zK#?r_VSP_Y;G)_g5I-7J6H+ASMlm%l;C_nXuOWGG^F|>Nw8wOMNvS%TpI8DWGkSwg zwsZ+*%6@k9PV&A=!OPn$bMOHNvOL%<1T*X{V19X%nV+dtHQ6uo9;;H-vVW);<(kx> zTK?13Z=JbsXCDje*w+HCD-6rGg9Y26!x?_nIaaCJs&V0olT<6!k3sQa1z};O^IY+P zS-yhAAG$XK9P@YSuJ|Chvj^x$p5t_bi(|nY1>53w5OA(E9j7l{(uW}~8V*zQ6a?fs z42KaG30@-S_QUkG>0MK-E+F?Yd4$UZvYDYrjWjUtLzmefC|J+W<2Vd~xsBE?RUn{)@8Y(_V&eVAj_RCVZzTXnCi_{h21N{cBvKA=3) z(NY{?F+9$7FlpyVYIUQ$z(QN(8(jc4nu(^KiKFAM2|avTGDcXW)FkQ3aNpeHJKu|s z-~y}xpoc?# zli_G9wK@3d=BypznU7d~x>4?6*)aLCreN9YXiQDU;Gx4zsYBo7v6WoawXc&brmA&+ zlxGaFF4DG7t}f)bic^GGu+MV2gzf_h#bC>3$2J$@knhrv?<8ZDQIqN_Pr-kqC-=%C z*-y#57LewG4>*A|w+P&cu>hZXaL@j9Q{^I`jC(TiE~6<@`;Tb1r^4LGN32VgfAp%W z*9)YMeaS%mzgszAn{*HaZ6))VTX>9Ij`jC*X2z#n#fPt6pC8-6j?@{Ml2}b`DYhi8 zckTMY>H@ZGDF5V?3J$4=1IB8w^l_7c#u0&Z#ZTx%R!P@2b(#J5lBcZ&d_1pgde&4> z&9YF&u%5E;gz9qRoN=DZu|vyu_eok-Cc~da#PK+`JH2MdYUgllzi)lVD#_bd0tD9x zJ~@NDJ$-K%{G8IGdSeFdCm99&Sp!5-n=Fj+1qf46ls_VWA8qD$|Xu+{(J_ zwCQ5Pj=;+J`S}}4Z}T^ltg`#R>N@vmsM9`xPeYnyTGue+lGr7&X3RxNi2Oz=N4D6! ztsMzJm1`GPwbU>nuuG(-{0@ip7Xxv zef{Aa=X{^r_j#W0^L@V0@B2}XwN%U+bHhUZJL!w#Z3B@9#gRQ`GNDtDk;j^rNdA)~ zQ=we%G<34I(3)ANj3(RhMC|}7M2Dk|h55M^f(gd0oSuozM&;mbj&$Va3!iapE36`1 zPd&`(+Cxn4m9K}i;c1lQwgI3z^A;Z|3RW|*^n6u&yZ4_5<=i}neG`_Z1ar^u{C7Bf z+wq?{Z5)P=cxC7zNn^vRVwR=E`C}|ulCV^>L#~y*ZK41|G^YV6>-_+2MFlJaJ4okq zY^qXqnlF?UG(YMn8hpWa`qJuYwzuwIGRzvXUc+rURD z(8^5-w?Z}!d``GUk$6U}xj7-FowTLNSu0iS;+9&jt7?`94+4^V>9#dS49AzEQ|Y1= zZ=ef%(BA$EG7A}Cv4s|EoBf{~mEs2bTdE8^O4y+g%W*9ki&Ui0BPt5iK_Rk`5s)oG zHtb8V^6;JMW=5XSY(XfU^%U3*3m{*iJZ1srvtYM87%&omFC%yLJ|LDzU^Wi=ttw|P zuealtuvWBiJt4t8N%fYfXjbYTfVhe;4Y?YhK*pEe7!Z({WktPH-hqa`RR*idMf;Ck zdLRub*E>1u=?*~5e#kT(MVa)ufZJPZMVHi!S=5xyh=2dXKl`gH6~pSX<#bhf4;j)q^*9rR6csJ=>S-vkhR5(HmZz89Pr ze}>*VKX>>Lf0Kpe0+(0J-YV&TZo52Cy5TAZ%tY-7DC2#m3mkd+y7SUPE)u0~zh`m_ z)viM}nXNyI7ac1)6{_>Y4N+H}^3q{YM+&-`Pe9)qAXB-`eW>%p@})Y<@-X(6vMJYI zSe>C}W;&5(c@ImKyT}rA+4z%Cq8S(?^H&d*En8#LOH}b#E;E{_O4eyYwq2EK?xg0j zT!^i2K<@qZ4@P~?#=ATBfV}S`FN0p!CzvKnky@2vAIA@`@96SQIU&+S$=%6GL+eNd zljaN0d9{fPt~GPE8(LpsdlFMV;kPP9TKnWh0W0Ilo-_k^s3Z5=2FRr(Y z8x3{|zi4@a_(D3XsM zLjgOibPbWj3p-3Uf-+Sek&PJB2OY=Z7W&qD5ekQzFUthQj6w;WorQ|JiVm?HQ;WKlcfu^WxFYSbq4$(BQlS53zvzpv8Mg zi&~Vt8OGL97&Utr^yRlRr_4*vLP^V1_vAy#-3lcFrq&5XM9+{ z=e(8y1~woKBCFB=isWE!3W*fWCVj7);n570! zO;Uon%HN;ao@gdxKdDLOZ^u@AO;w0ezp8VnKfVL6fyMOgt|NZf@{O6!<4G}mWD!ue zBLS{mVa?Ql#mE$0qsT!`5TI?G;*_wD+creFm5l9{tGhXA*GBOBt`Ih{$v{RZheFQL z5*T`SrK-dicl}v$*11!H;?9M5Nawx48vgjL$weQK!z!CIjo+}!5pvx{x zX1!En`|aB91<7ou`2zBQ^rC9-L{9BK@bbYM3SsCCNIP%|!=R!*uv7f`Gv1cr^~cZc zaQO2uzfKlnMXi)^bdh|DnuJu%nt86p?1s!`i5xd$5xb#qkiARIVI>eINX;K2r^bsa zYz)C+JUdS)Wr);bJwlo4EBmp^(2AIXgjTA;AQTDXgO?2*g16i)678KXol`BZ<3HGC z-`%WAw1{inQEG=og}Xib6(V#+OyOX;+CE_oRIe7KfX!*$0uT0X_& z~^pz)BLZ(upWUsq#k9;vouvSsB34-&J8vIS}AOho^h0Ujk21&J8jQ{rM!Y-kSy;IEc-Ubd38D030{<%|vIn)YsfBLzUy z`;?KvT0=0;Hh)da`D~2GN|)HabJ$i4SQerbArl*Ysec{NP09BPw8gRtC3dIRKeaNI ze%1KbzDvrL7KeaXbG`So0Jt>nNS-s{7ZK}i8m&UW9mOdYH?BKo5koNVHe07>z8u7@ zESzWH5Ck7^PP++IS#opF<4+ro0KNimGrv5;f~m^_#stiS_pzo4D`52@Fx*lb?{6d5 zi3sp!!HOEvD?OxV?vTE54T5KKGARhOJ3?3YLLc;*GVSc^+PkL}_p7!h=JkZjmP}JK zCV}86up@ubP|ajWn`-WC-Hlv(N)}`SPSPTsJ{V>ydLmd+NjmN7MvcQ zjl{(!KYpi`RaOf=!g3x*a_=8Hs7forP)H413XdOGURWdfiDusY2{9jGe#LW9Z`4tE z`Wq6nW+s-}FUB-+FTkEAxrQmg832!I0NKcH)G`DZnBN@9G29TtBPDlUP%6xv2T|L5 z(I)y0wG9pE7#}9G!qjV630h|>FpXFq1WpCi1L3AL-2t41+@uottIzQ5xvW#n0c9$C z@ChGG9bA&hd6N2|I>A?mxu}HF?M_b#=@Lq^%!xFL&bN`|1|<22l7wGP`ZPgE>^-sN z)68DC&E)(3@dj!PRq$2RK~Vf-R42GQ?I!z)>@#p2r^xSw6A>O^*=Xoj(9rKu#cV+7 zGN(uDe(}z(Ln#}55<(s76vSWRL|qIFyP#O`9b6+lc}?X|NcM0f`(Zv38p}@ z#O1${K|Zg6ewoglf9}81a8XXf>HkWDl!Wg8LLVSuN=|~`{}u@fvrgN_pZ|j4mHec$ z&|s>Gvyt?SMhPJ)Q~&r;J4ear{_z)D4m3#G&%gV@mG69a#g6<<5Pk@j#5h23E9&QS ze{Aw&yYx5%;(#J%Wz63e{^>vQ1rpMKkuG@Zi$_QzG=j7y|6e30lV+!dGSx|6us8Pq zY!b-tP@V-?9K_Zcdn{W+Ayd6x1nEbm^ugY$+e{ugwv*QV^j|bkVHYJmQ@gkR(!0$h z-Gy+)Y3K}Nd*1wGKPC8Ql^@blGM}D?pDT=|kCgkxC)%xU4^FwLJ_-wd&l>G?!p{tH zPHI=9g0fU<^ww_Tl&8_o=N8Qv_x>hW+VwB5Zs~+8mM4-saXc|^eK0a)no2UCVeS>c z7Ta!pZ8IE-*PBLZSiWj`W*dxG^Z2I!V0Rz(*-X)(qhaLH+PQYEttty&FVEQ_P`USS zn?FIZ+|Me{TFWl);n*fH_X$epxD+=dpLu(HY0BpKUK>cHb^-yvZt)@~h9)P&QFZ2va#!y1 zw8poVfdhH}P{&S$;>}+-T>G}*=J=q+Zgj74Lf%blyobVt5hC`9Ox+Xv57k*4Ap-%m|#jsBgCrw+~4f^KbAeFtt0^E)@u8| z3}q|{O3c9iJJd=sIL(0Q2uL{o|9KL$@dZmah6X?orS|od(t@3}PZy;=qldvf7u&9A zX9{S+NWT=c#l8RV_CLRA7_?XgD?xgixi11$KHLq7iaBy=vq^?sg8lWuYnk>~+>y*HM5hqm&fp=k z_^Y_mBsFx|vNC=f=FGZg#|70l%@P99FQ>TL&;Nw4KU^o#fH>x)--n%O-lGfxAovxyw7xfwru+8vvv-PxDRX=I`6WxlR zGFqyBPcLxvU0Gik8;pFs)c*ZJy4gi-PorcDWrRXTqn`e&ff^3z=BhcpL$Bc5D7 zs)H2$B-F=1gY?}=^q^dFYgiKD_GfG9QDaGR;k535g-j{RC_M6zSz+IQ6yoo{0e2Pk z7mxdw2W9{iAjFL0|23$o3WCc*mWTfUwLiie5bz-X_>aFwJ&0=>=t|&|@BdRtew;KB z#2diQ!*Kt?IJxbEc&FwkEB}cGL-Ujb*7}0Fl>aAvazIDAx#=ts*1EYh3eU?Jq*!bl zvW6LDA=PV)R^)ESseRM6@cbtZ8D%N7GhaK`4q+ZNslBRXO{7+3Ex34T4D(n#qy+c`<*g<(XUX`_&YkwcCXqoR9mkq2%K`X}JwQ_3TeCP@Kq;8dJ#T+7UHq zOr=`cfW6ix8q;QKq?Xjar(MVQ#%p8MRBPSpPx^U)%`|E5EUiS6-uZ0h4j{y*|zID2^rvcL6lr8>BBA$hrR!n8u zEZW`e_==h<{x|xrOHJ0Z%z!-U>0ogkwRYq{#a(5GR|L{q1*_S;1$ttpq1_~HlOuKO zmw)IbA_mSaLx~Nwyl-E(^Ud4bs^@|&ykBVb#(R>h^ai19J6F3&UGKLDs&Z z_2y5m^*Y-=W-ewFI{n%De)>`FhHGhGS8-?mG+o}WKS}-{O!r{t|JkPde*k*?zd`~c z7bQM&h6s&8&&kn`4`3LcU5E?~j`Zn9`3`!DU3wDcxo(}hAqu(G-I#5c@MWb}D#EWE zYmxO2drsGudfPoVr#8>cS!)v8$eI=$+^g$977blaDMX*|Hho-&SEm^7%JbWuuigF+ z#3M(;8@P@7>?AFC(xPnDc?Fn;o32*KuH1U-JtuV|T`#&u>w!a3BMZXxECy`@ga7He$TA-pvY8Y<%OnQr5;Ku~;C}iz zq2}zK`qhfv`7!e>Ux}XD`rZ-lFJK;^9Jjxkk#q0x-;8|kJG(QEiJyn3I);6Q&q?-5q5{lYEL{3oODXrMLm8lzBy(@6hl(yMHI zKP@gKJU2MlT5&8|3I>kecUM4caM$`K7Z^{543ST|F*oYRM6rvR83%G7s~X%~oDmtH z9siJ#715aRcb}tX&@%Jx#e?sy_QjW;s*5fzxho0y8DnYA{SkL#D$R9QzW+06KbiaE2#Wt>ap3iQV5UH1EVeRyB15)53|S@;?7#C@ho+E3N#9tlVsg@ll6w<<=D^td zkj*Q6x{>joXI&boZXMpi$>A>=TRPlV&67s$ls;_#%;4TTE~7QS_`c*zVMD*>?UH(X ze#v)on)|8`;xOVht$nWwyXV_HY9p%sO_iX|%FtafH^>@TH&|4&5j|C>`M_`A5(SBxdE2zR^BqU#E%F$=5t*(|lc2YjQMsf_Z07 zfoYz_B5~3S2K2uqeL{H{|2TU=WWR=iP2cEV%s6c=yQA%>-eLQbf{ZP;dxFpBMV|k+ zGlIWyWx0Qb<7_d@>({R;g3oBG_1nK#)F0)nf_X}!XLC+U?zS7OMi@|+D*$zOKm@qz1KY9QO=#1;5gxf9fF!el1T>{ zLmDXx7et|vkQ4`@thudSkCU{wLcn*(@KEU8(Q=;4t2jVNc$dB> zuh=+AR_cXPqy_1_()#rhD)bWqcCwpgM)X|cR(}0YBu2^-<<;MQ9QBDRtFTv*|BQQc zY*NhV{g10?(N;1S2(A7jv@?`+f4s^lW}oD)n6c)<`jtSLKV(%L7mOk_Ok7wjr`0<~4 zXy_cJ8Sf)lZlz{$9M;8WGb!k2xH$#D73Av^KN)JEtJIoo4sDNtSiV~7_GVk+?9U9_ zpW)EZY*`Vo$N9%`DMV$H*n;j$)>b`(PUQ>#<9-+*iwD)ho}bE0F;eCX#9t7!ea?%l z|3lLhW8|1L?rJH=<3}L#lc4gAY|6aKsY!+=t(wzL%{E;-DAy4X%TF zeO(!yn4e6g=4StVEKaWBJwLW7&{Puhd<)*Qg3nOAAUlXQHCab4!B$_+j~x8T;Nx;} zUHTy|as(WaS;&lu(@C;>Su`UxjMtVwWK^9cqyPR7ZC5y?fj*Ax;5+)d6>maIm44~1 zAMe!ws`3Fuxy3);LY}1hphV= ztIhS?!11`ozMsa;*OBSnw7iTE=8@iuj4S)tShlXJ^r8D+*1#*h+i|eWHhSIz$?(q> z5;R=K)gK+124Jc_X=)z;PP5XM|U39(>11o-@r-G$i91COzNkp<(I=rJLj|jKa8xEj? z@#@p9kcsT{)~O^abwp7!T4Z3s)8_wR6eu6aTDP(;mBLEk-dHMU7!4Gc-6!4DfY^gG z7r)9<5!0`2%0TZfxY(N0)#@>!RbMz~9=pi|`;yD`*OIO50g5dxWy`=rq2E9ux~zp* zdphk&iy~A*H`?7X7#q;%=+iu`)bo0VG2E&!@X&qo8!Echti!e7QNvwz%V$Ny=Y=KT zIG>JYHKZcnz!DDWSvdaG@;qf7-$-n&z$7Bw8$xN#%fHgWH3*y-h^(5`*e zGb6iwRUj@-xay2qjL3}m^{{|hrw$zBclj6<$8mDp;7><}y5jZfm1MB`7e7KKDvM(o zh;w=H3*XROBq(&yrq_opjY>9*+^a1{g?(N1>!eFXm$t*lsjPR%^j0r zk+j4OoTI!YuYgiaA*GGo*W_ny06#HNm8jtU_=Y4Z1OI(`lH2LH0hQPm-Iy)+%6T0j9# zWMsC4L@Uq-6tFAO_?)2Z*g;g&E_~zdu<3!tR)byrA^it!1~8cVG$4*qxpKV&$_0nB zgzF_D?2@Wl{ltV`I-^nJVWGmu(mCG|Sz!4jw`@ay)`RW7-Kd4H?KN(|7k)D^&POI+ zF8C;K)%m|o$D0sui;!T>>K-r zIsS21qm?ZB(07%u-UwDNgX^yy&|i$a5p=mwpfsx2slq^^PzaH5u{ez|dRSuY-*cbI@4(f&^C}2*=+k12!;ZkqkvDd7{ zY1GmH$s+6F^0@}aX&et9)}alYPccfTW)yGSp#N@?=YJKPr%2u~{RViHovCmdnZxhW zH0X7s)Db+mheM*FZ(ojznwV(s^QT0^*=1iWiyaBA#TzJBw%P=KlnUgJHVc5rs_G*u z3gGf;bSP#?(waKXQjBle+G24K05eGdeq!d2J$EJ*irff@w9>0vQkC2%+ zQO-D1fH)u}Eu)Us_A8*xzwNQ0Tl3#@jQ~Sw${}RVaAJq$E!)Hla4$u&x7j&;q3-XB{0KG_^gCUzo<(y5pX- z@+0M@bX+DuU_cIoKZ!0=e=fPoe`OaOPrW6!PJk(2At(*!-_1efA5rNYZ<%{+G?CnJ z^wLo0P@BIuqLjrQnmjL|fi0%Uu0*yt_9|IS8ig zu#g9KdU+(XZ-#Ib;t}JB%fY7=98}$pq@1?)hzUlN=JhhzpiwzCSB7n4|va-9}_4)#AcaA^kPZ0kTL? zStyCT0jQBDKBgF?Vzow9@(s%}>QNNV%KE*cr0Kg{@WB{~qS8Jw1f(q=7v&#ok`eaShF31{I_lbQr8kG(K zY^~z;AqzJ7q^OG-w1|*g-RKNA!1*97KTADp>s7&~b=3Sb7zYvw=5frNr|r`-OEEgjMtqqauNwmMO{liE zN%cHo6{7F>rSE!p&u)P+7(+w+Dp#qoP@EFG&L{6z594jrh@Rb$gNae`vptRMTlsy3y|J@Q z)A9Ny02PZ8SE`d?&%zQ0K9Ai5*`t$E{j5$nNId4hNqMx5pHMMFH-AezxCUt93b`KL zDo|$)(a9qxrn8o-EaB>9b>Rt(lj+>#B_?JiWA|A&HM~YlrDn{>nA#*~@i-Te27l+vRuR zn^ER*RMyUQj=+V*UPiu?pJ>QHQ;BdiG*1U3svl8&gG+HnZFK883sG7=uK{YCy^4pN z67gqnVj|`^pa!p5(8oAnJ)O|%S&O3`j1pPPWxunNqrlfZG(?Qfx|dJ9z<+FlzME$B z&dMV?@?7E7RT0FQ!(~C8uyDoP6EPTX!35z({!B?X@`3kL#@X2I@|4b4{kVpHlafvj zZ@Taajef+?IF1v&X|ukQegoI3%h8a1>l&pVVx{%Q$RSN98u_m9Nt>jme@b1JrkmD;S|h z7ymLXHYzT`Q*Ip|t!d&9w@D)V#ce#S zlTnbzYX>wqQUT^Rt~V>^;b{vY7J+n1DOMMJi>TW9XxWK%=cEM^3R7Pmn7m?WD~U6) zVy%=#K2`6t(R?zt7NvOQCH~!lXfJdJUL&FuwSQe9-*zX30j%p=^~!fvzPAnNV@SVW zpt%=@wp$G_&Te7;;IXfbGPK)Mt=_Co`0PO;(UID2Z@j=Y%L}OQEpUe2wTp0sN6{X_MLGkM!I8-MiWkPwO`0Os?hGil{$oK(04{pTZ6|tI?xv?!wM)sOyJg z)Z)GBZyzXP;-c{d!d^|x#OF0)2bEmykBP)Q-_@5881qW=l*EhVG`woOeezl>3L z6eB?FwWVtKxDn^=LL$*^UyzS_lfBBv(!reaz%+bzJG^dP1kw){Vjois`zQnn%ozc@ zi)0^wtLszB(7J}|&4V?4RW-Xuo~8%9C(U`0D|z<#379JZm4zH5M`aF$MTt4;qV?6$ z@4=^{E;2tkJhFBhIpo0M_4qwQov+C#YcEHAvC8k{n$VQBtm;omwWZ5jlTA3jSz0c4 zxKgt#8kO_#(Gw(VD4^lVaRu5}$3|-rx%CBeJYC=JxQ#s;O~$KetmMs6w9YM$L~)9} z#u0E^w72ksa=}ZC#bIjuF}|)6VIeZ#)_9p*^N)v;_4X>grunu(ij_Gh zcGE^mSgewGrwX8CM+$rLTA9CZ*Uy^OR1y+;g42oHBDzqa^bzU*`;&th%%c167=vBs zf^}nikvth5bQXKn7U;!Q78Fiwh~aGs2tXY3p6BW&>AFVX19wJ?s7`cdJW|BeHagSS z40%p3zM8P3If)eCWDLK5OHi3TBr8d27T;cWXNq!#?>MpH%if)&w1xM+5bD6l1i`Ay zQMYj-y`V@hi!u`1bFI1kuw(}RGL4%deLdoBKn>NMvspQu2z^w$ds+T|{+w(2zxfQr zABk++Y?Mr9CM}@`?qdb+BtuuH)U4I<^?io8ar0yC?!BwliTH1C5Y@6}GAAxK5g(X0 zY7=d77SqA=3!D?s6I1Q$w3kQvZn({{wz8%!^fCPkx1cN0VbHtD(8gJOAw#1d6e>#%Qgo~171Kk)9#zb(w3>|u&Sj44DaLs;Cw;=! z=KLZQyuQ6l*o)!QE?D(JdDK81wil~#Y!kmzIWEtz+j8{yV% z-LY`l18K&Cf}b`ZYCL^Tw(E#`WU^HKGt1Pv@R22;qhK66=as;kKsk%h1^nS}$01bA z%$vv7w4k)zy3eNL-<|DGf9jPmyaP-r4k#MTQGz^DRk)D<`04H1!?5`q5KElZ6Ja65 zCLWrqD3RIP=-!RQP38p;89r0oPyI3Uqh5iwMo9VUk z?bP=b@kYIVqF!jLwd%#iV*gkLFRxf@6!75p_u2GZCFJ{%QQ9#VRrdlz!E4qn@9!KHn@(?Zm-RSV1gT?z!S2$OBjCQ%i6pB~1D z7o_=4%nzHG;cMxOP9tr5v_=@Pj66$QEiLDBO3~GyaK(+B)haxT6Hks8Q1@I571RJq z3m+F9D`w9$qo@Hb8)mr=1;5hW^aco_Ym>RfMxe7vTj(4e}A_2y`}!P z7TQ2Ubs_Om{t0v3!nC%M8rqrPFjU1g6fh#5;jJ2NZ_~QWBApYoAfp)izV^2}oRx(< zv&W%$jWQa|ixy#&MD84!a{)xE+k9_l@{1N`TU?`lb-f&!;mO-ZPtje3pBnPtr2gzo zWFy3*tq(Jyda%eVvU#SmVo!4I+PlOL`JeDRH>q8LSot~{0*Kjj!g_B#WY1$w%x#`k zLc6)w?Hpkj9eXPcc0=(((mUo@MqaY(*uh!A>=GBf;{+-li_Em@Q8S5NTSKvVRk!2g z6FSD2U|c^uk&gI~CuY7%*C@Yn{ehzUDPK~6Hj>?kf}6j} z@}On-KYc@I6m(goadN&#S=y8Q90}|zD{X^6lYqJ?=JX3oo{axONBDp5-kgy}{J2|x zIE|Qe-Q=CyX?(#D@Q>bL0MjX}I+@(*WXQJs+AWyGagn|$B{$fo30{l8tKV#fJrxOi zwL{VF(jHxUp-ZRg?y6ZCKHm?Ap~W0Ac`ZkDD=lFWL1)6+Py{eKPG{VlMpZYu=!;`gZgV>OYz{3HUmH!2-Kqq$C2AT7cqnOr;fX3u1L)wU(Ij~s6H_Tf%u zxpxYzZOd?nrfW}tg`w|;#*EpJ-+)U-!we}70^hWdRH`3@9) zrAxuaQJ)e!C&vEpo~0&>7!pPPMp|fXNhamAd6mCw&C&|wv%9c!?-f>X%PTYabD#*3 zj+9a#<<;euoG|w6AU%d8rx?x9cDwOOCP22^>hJjc4q3 zM1*72eS|pVRW{;13T`3FTaVY6&Vw)fco2)+#&NeL!?vR8l0?jPaTITiT{%j>4#Xp^ zS>f}fTf@Wm(^-}}xWT?7iW^v#SZKCKl6{gR^C%=WO$Ra8yuLI9c{5KO@-n@XaB}RC zqWj%>t-b1^9Xuz?9cj&W_Iv9fG*QU*lkgC9jZ|y03ECH?&b1gHL{t(o0@0V3-|>`s)KMqv4z|Ir`+16YRM;Q|{=Gr} zgO*3&(q2z|LCcy|+|h0xwOi@PTa_^0Xd`h>xu-co5JJyVFo77%GbI>b?3~qAh=x`* z-_%AC;{9M(-l`1ew=xxM&bf~(^?pRF9lRg4CQ|qXr8aAtiHUkT>Z$vcwrGLmMSJ0Z z0v-v2azLB)$zm1SCNjr!rHvLgXp9#d<74fMtsDdAZ$Kkqp4o^JX4N|coDY3ZPFwq*+**I8%a#8o+&-z3Dq=C$d9HZm7~IwBS%=G_r1F3hr<7lJ z?#PoyD|*{&6FtU!8h9L)=lu6uxS6t?^V!4=JMA)=Xqa{#|Q9Z*}}o8nr#p!$(&&)Am1_f_&w*)`tgE66x?Oi zmLti6L)v(al$W7=qrq`t$D2Mn^x=Mk_>`vKzASVspxnEd_c(M_^e@(BFTP6dLWg80 zeeGveX7z-;HbM}}DldbDZ%6nY_0G>J?rVpK@;1<8kgdPy_C$}a2Bgv3RQJuE*LXRD zS%phw*9#pAHui-~mLdJNb2&E{+xBS7f+Fa6bYlT^$uL93wb{{tQ0Gpmg?QZSM}_6s zc;UYGjHM&|+8{wQ8aOu)y>d}#E~@Iu?epw99Xz74P*}W{dTIWstbSHjt(y`l7TRO? z=<)jZLRzU;!@nG*r_lTWgbU z7Kk$ZldjWO62SQe)D3K5DF|T z+`%Hp(xVaz>R6NuBdikyUw?mUGE@CnLPp@bR(dRB#B{Cua53U0w??z00Ph`@5jv+u zRuWw?8&Zc!ZccKnm5k}W3ia?_YFA-Oi?-3{1hujI3=2y>q$8e%VQfW9K|n(KEo6(I z??8aw$?=TAi{bWmjWkh2<8zj-w4M}NF+Y1@2WRJvY~3q@&^=cS$c)xFQe_AScXC8= zbGFt_#P4+TgoZm2Pk^+Jty7PQR|(pasr$WoILY45^z{B;TN6#GqKwN4+nY&iv+Gw; zeH}y5EGfcv5b!}z-B*Oq8As*>4cTGmA47NIP~aVy`BlX0_ou3-ZKwJ4IR@JnbpmEx z8Bxs9iz(F@_EIO*cjr~4#qY-pLa(Qvt-$YH;52k1oa&?}x<-?Ry^2@7M_1%M7Du3k zA=P#br=jB|nx-R-63HPfg^8FitB!|sJ_!jytK!wmuDyP=2lXO5 zy~oksp2*dmtESo{u~ON2KxsC9<$|*Dh{j4QD{LfSiDs9A9)2Zx$uuJivGa%?a$X11 zUH8%a;w)+)NI@HW&YDt}EX(;7qNJt}fHg?o(d!9slq76yyliY-nSX8#nu?I#)2wKY zM2}-|{+dVf?Rle2p`D{cktnlbqYYuBs&=hdImy1DG=vrD9x^Pt;CbV|qHxVQF7Sru ztdut#u=YJ3o;qYzv}p-AHG-f9{4~`+HNV*#etJLt6ttDOQn&Tfw(87T3vs#bQIHF# zi6FJ@b42&q^3^}&!Bb!veP1j=eMsNq0e9TXR3g`2TK3%$?>gyZP(0^} z=!%BXd5YzrD02{Mn9z)9L&2}2RIY|^&)Q5=z5wy?J_H!2nQ(oh5h9VUwP=iu7uEM$ z4SWni7fJGPc#}M%Z98#6woWdBesv5ckQY8C8jP!SA!v z^PU`~x}x*&n$a1vnrQL_3nToR*=z@j5e#x!Ng)vwT6UeGft4 zVt89#CfdUpEizB>^QJT@Rj=(s+_+OT&6pCJ_VR6U9usF4`hq)9Vac_NK&v37)9Q-) z(>!=Ru1N5d_=u7OnLsDW%~VAlws$kyJ+M=31^TFuCRi9>tbeBOGqq3 zox^#>=zDV4gnfZ++ljm=9Mt4D63B6|h=y%Tphrenhb}>P(juzfehJb20yf~UTg$ao z8UHcdlBK1@K5FTJrX@mGQ+772X<^@TX}F0q2_3oERPnf9wa+O-D|Dm_e9cmJsO^@9QxEklN>#UUL0c1O20E4z zCZ8g>*n(A>HYY+yVqp9TORQ@%CHpat#XrZIr|mjDk=+@6Po>F8Um8iPf!=q8E&cNW z&h?rhR_$_*???(Ml?pu5kD9LLCV1z?5d5UzBS_>iNWCwX#2!c8v8$}7ivum%kGzZA z5|~6D13Pis>pkmp#tOe>`HolI0{X(3i$R9 zS&P#dJ}szsd9Z}-lIx2pS4LPCskII`k6%~UfRYX?bGAiEo@CfNpLN`eQK(04*S~(+ zAsG9h@%TRJxF~PAhs;y1Bq&qTu`0Ki$2&VATx^_B`Wt2v1M7_UJOC>*&r^!PP-6=n2UspL#HbB%ql)28Oz z1_kyGNdq8l5FnR_$&JA~8>JXF>$gI`!or7ui3ICC<8ekw&J9NiVUZN*8{ccJvGkVf zdjoch9Q6pN-IsWM!z@H@4o1d?b+s|jnv2fTqR~8C!Y#RUZyPLBX3ixNV$VS~RPi{P zLq?guLTrnibj1d+S%er+2xcCfAt#gJpg1*f5_VN5Yq1+|l%)}~18Kymx3(H+hz!y9 zYs=?I7~w@=zE6733sXc<@bBa%J2GMTgd47bw?@<#&@!5U)KDVYQJ?rh=onFTcYrw%;(dpdeW;C!wNP0-aailSPzbpWQMJno%o&0e9F?pH1_o2SjfEcJ? zpcbYv>w(EPhQ0gsy>#{_QbqQvRr>oOve+4J`PmtCRI-Qh8y;g{W^2sz7L4z0W@2Og zd7MAyOXg&?c*c>6d<}S`w{v@D?;!6@Fj{oW(1zFY<}um49VtDx$ma(hN{2$(h2?TXSmh@zEt4h3 zHJx=tIsV8JN#fE}+sw?l^`#pnCmoqOR@$61( z^{$I3J`D)9XI!xIitxu6#*W|B(WvmB4M%TG5 zMgP70K}}5dn{ZMr&$bcNwdFmbx;>Ew#A3Ba!Y94_t_F^cD_M0@)gmk0%Ao3vK>-7f zbtA1+LibAZRWDI+vGZHAf_7zC0{$ywz|eIUf^#OlxW~D3<0NAM8_NXpHk6@u9qX9^ zF#y-wa9d%H0(;}glaN;#h?_q5ggWcl^`{lLsy6}Tmd5;Nb!XJB2_FaO#6<0GxR-;d znZk0D)DzaDP1&J>JLma8n}M&Jv!WEt@n(JxajB1Juu0{3h{76@3}O3rc3RsvIT}i% z{+oHQQ&ZR#>^W?QUC489!b}p4U_l>kw0;l4#;SGw7~IHHZ)n^KrF^t3rq;lu|6Jcp+QqZINez@6g!3Z7Hwu!n>i}Fl;`N7jnjAaLGF`>-eGa+Oacmm~ zdS**4=K*7O+?ZSs-*vetkNI>8O($6<;yQfQ^l>yNp$+2FF+Mj^bhL}}bRHa=5ct58RkB){7VV(Yhly*i&$ckCEA*y% zP_Pcpbw0YhMxucF$gb;pOkfT^j@^39VKgC@G?=Y^UNUVv(1PobFF(v)q)atKcL|jJ+?}L( zgst+eEuQ%t{EkpNY@8q}Z~cCFZJa&xiGs-UQ5!YRKEJn4fV=_*>!sF}=wJdukQSpw zft{P2skAqHSKS?piXq+6oR=*7msjHuxYiK-npji%g=9A{DsB7Ar;r-$8DOb8NEYxgHDEm%6!SqLHdu0SA2p2aZoKV zWi3}^@n!505f_IPN~Q!S;W|jS+b1+qN|uRE%fnkS@`n6q4-GI%B7O^AoWryy9f1v~ ziwuycYCZ5WMp7!n#|IIi3o8AEQeQ@htCfS$OH{wqef%DBe1;Q-M%CQyroR*;wYTMS z^pYag$u)>3+&FA`zjd*kKIkA_KE?70-v#^zmdI{8g7ezn7n1aXt|}6iZ{Ya4>aJw> zjFXV(P~G&2Y{dP%&y1~`Ny-KCKv$Qu6pnZXeL}D8gYJZ+nD{kQ3=pu-CLbASUw@ta zXR~jgI4fMgUj3+|K=SN91O7zLj!$S?17e@rGrnz;8ydHm5?j56gs8K2L|JVlCY4aT zho-n|Q?FvVPd`W*FSM4OyRJU{}S*HbGMCr4^*&L%ggZY~J+y2h7+h-m|C) zzirdzA7t@*;}94*2X@%#5(8%BRX#9ZZSOp*=u!dRHsrZ^cOHk3lyh3btX^-AY6@rF zdhJmEf+Zyv5^FD?1ly(l0!$Mrx)y=_1IdWCHKBS@@^vdO6!v-YGY`|E?e}zeu>vyYx7%CCwaDIv~Ht%wb+EWV~p}8 zJWa0&o+Kfm(Z|+TzOa|J0$$+T02WRynB{vtp)r1slld~$gA%4u#e8?u?C~BR$2h6W zNL(y|TEA*AYSP6Z+a=I`Px+KUik^=Fxi($z<#vt3I`GV=3_||JP=}(@a|OPhn)?>f zL`BSOt$n*`xQ^DNE5auBOB!@yxx7ZXfqc}Qr_`tRNsfwK>F`r)`+DbB~i4q#-bP7IJq*$_2>-cOoM8t5_i_PY>YJ^~T@4@WdUIPWQDcY>G ziJRZRr;JLZQ`gQEh2rxPCj&wJ%JuLT=w<3ASL*6cPhexAQV8ZE**U zyeFB`F;`Jb3Do)JE_Gc8m$M*g^?SxThk+UFxXcsWfe^ryshLt&kpiDH2wuT?PM&8v z1CB(?nC`=X4S?RP2H)AKl?PTNaJ>alklRcxRUcZVR!thD;&!xdGz)XuEErmrx_ytI z*1l4j)9nmVBI(}hYbiP_w+U9}Ni@=b45@$*et?lwGw9?pR?*Gh8D+k9D`=wG+v1mc z_i>XaYjKD4yQ!k$Poa*1%fQ?s^GC5kS33QMj(x(P4~x!dW0V>-zgY! zOvT<3yJAiupRw+;1sh*5-hrDM1Uq>My_=+*jbIIRhUmu3{l<(+=W@(mI_m20gSF#b z1mj5&t=1Q=>g2Ia1;`9Af4N3ZQ!lubqoG7;+Jl)y9^(hct4E&C2kWpMly}2j!b9s} ztF!inD)%4-$aJ8NwQ{4lBGJ&dROa{!;!JpbsHU55=&BF-w$X0bQ;lXtSzfxhoT%j5 zWKSAS0q83z)vbhpe>1@7soy9ietKtZhM7qnstMZ0C_lPV(Yn}ig&?ZrQ&fn_J*zxh zM=-9fFYQrBkiLTS-~dPq%ntEwQ?hlxu=~G)+L~p6D7p67IyzR@LY=xEwgr4)Y9JK| z{f(SyDS&BvG|(&Nb}|{Q2DBTDzOF@^QRf=)i`v&hgLHMgk)x6Ze7*FWOWWn(y)>Vr zkO|`a{a=_bwj*l1#bC*~?Pzag#B}1UVfBc%Xi^M~3QKEL0u$-{AJ7Z$g^T2B|uSzm?G`8gE(gpb? z_%^E=W4|ky3I8$V2|km+&`|1OXmFFAnKVQbHduhPn+#o1-)L9P zp>nQJ8zB`7A-)A4{Lp8U~6Ju+m~~q zHhksQ3`tDEr}3bhWanA^49qVRl|AdfP;ob_Y0!nC&e*G?nTtK=ofd$IaK&MwE`I+4 z*NR)q_7x`vQ_b!JwL>aim+O%+c=L#cevKgxOsHzBwsKxG1iUXyU1a#cy!~MDL*o2% z75JB_T-#Yd4c^NaZ4?-^5ShqafZ^}1>#h}qlDKuaoBG|IP6=SiXCkzpI6$oNwjWvk zt02^QzzDn!e5(meU676f1*`-gB@4sFHh^%lP?SXCMwK>5y5Ed-XuT7qkSw~dhENxS zVR1dd9dJjs3Vgl*)VNoJZw+-?DpPScrd1~;9ObXLEdmwQmyY)Zc;|tdQ<8)oqz_;g z`zs|qiqt8a5FZ1HIZ}1N&M2f(JwOmf+O^tEIV2R}bx$y~;?f*WMF~xG;GDyb#u#0T zg)~a~RrzRgBNOtACJ+i|?7WB_?WHV)dcq%5g~!Q+C+} zERvdHJTOHv_|?9~yJfaon}mwnbsZDY&M2>OKDl?H&o5WE&@576GD z%P_qNWU&F=3r^B-ZPoKkM{DkYJvId7` zP(SCfh4f4J0xGq=Uos)pqg8Syj{uE70^EX5cs?T9PKSy!Y@YoUyyRCh8oBus=iOLW zi|l#n_YUPBkV;qF7QVCbNJa*--bqb>ckLm0wol0(wh^C`Bq;mjNgIdzfoZeNl&PD5 zAi-rT#C7nFla|CzGXzzH)#{yQG<030IQXfZl{BurR))Rv+du9JR2vX|&F-=o)p?`T z+vJ&Dq|0~4{3p1h-oHuxM+!g@Ax`FPdcZ|rHeivjieo48XkjlMhurch@{44uX7~2| zMVGMkkAZebGYBY zhJ}tVh}g%|TZnsNbn*zjY)cE|^Zu)a#B^d{l+?Y-r(qqzML7d4B~e`%UJY3_)LVd2 z`-VZjmE`Fe12y@GuD=V27?T@B6Hp5cH89&OL>apwvI>X!&$1-MW4l?Tz*Ag*woI7Q z@@T&t+ygey2A<_Ku^3lEap=gJI8y_j-6ABFyU|Wumzi~nJfpY*F7avrXTMJQ6K#M| zroN(#9fLQmtR1r2N5no8+#id1*7!BKt=Tr93i8*+pU|+`N$yXNmENb!I}?%WiRD` z_5{#142EA#hllL7xMn-JoX;(Z9^`YVlKz6iF1T*Y#}w%&4P(KMo$`3t6!0%pLkaSW zL4Gx;z$ai3NPb}-8i)MATG$Asxat8o^6v+%ah6B3MS!ldj27~m4#E|8BAs^z41%#Y zK9Df=n)hsOJc+5d1rLyvP2`t1vA`z4Rp#j!GP z`<0NBkz|tXi!g4@D9J>MR9|hW&itgUTv9qJtVrdHL89NY$Y( z2mu>itAObm5X+wL-e|Q`{>Th7=Q}yCd+s^+e&?4>lVZAUCq7Uw4;+`j*je(v3(bF$ zApC}wE-in6x$359v^UPAE2SOKA(ZMO7}bYFZLX~lgOIUPJx*8F8|z|KoBUFrlQ!LI z8klGt2g41pm2)0r=F~CLTsG#q*EHl;`HTKjA5Fo8Vd}QyP%uM>Jd2(hByiRkgHS5r z`mg*abQ9zeE-U{Ta4eWmg&x?ysFx;w48vMP0w#V5#Copb2Nti!_Na_ioBsI0$7z%A#Sf5teFn?_`)o~%!>bj3 zA-9ZaRm5|HVIT)02V#ZGgHnk`lN z3NH)JK%QvA+#u>^oZLO#-?N2Mi$Id95dKF$QB(g70v0erkar$INJ6y>^T8aa?jYwx z;;!7BomF0XuhzGc<86!N3F){75AMOHgL(v>g{ntjFnz=x$oq+O`f5zg2vfs#d5+LR z-2A)oz(&Nokk1yR=~-XJ2fma6`e9l<|-P|^r8}PELSiolx`fCP7h`caZ_&2v9cEzi3A|L z&#*|51nU_>SBHH$0M_I<&^INv;81l()YpXI`gg5P@xj8`+kI~TDK~JL#_LDq9hp6L z>7bD@GQgqrDi`;w(M(F66B}yz{NgyduOy}J7O!RwO5q{6C3S>V4K*|zr*V>R`SYtq z-r6E#J@3}SJLH&8&2mJ#-xXc8Mj@U3XMth7N|byy)eEfSFF5&nQ5OSpX8;n_2rX?+ zl^v)c3bYJ}%VcS+1<#Zt6Y&`~w}=VdL+uK-RpB^Ck%|?ymv8+@(SuTDP(&T`v-Nw9 zx3ns^JSjFM^jS3b$zU|}`R>$iJIn#{ePoU2so}rWhYgdvG+t+LR0m|^L-U!bv@NpX zqUx#kTvbHHJP-a=S-UPUB_fsXZ$^+W{Bh5+DGkv+f$fS@QuX+JU$@2g#;^R_fz+he zbJMOs zblktVymW0#U)U_|Y{z{SesXiF$}64{`%zP2S_?!7$7$&(5}_KZ>k|$icJf!s8n098 zFxzCRv?IXh4`*}XM&_Khz4X4Ge~U9-8ue|=S6ybyxmI!z;bO#{#es={BpfPWqGGc_ z6H(3MG1v1nEjJ$&l^G+wYk25eN!-a=p;L!I=;HF&oO0uapz4q3{`K4SlC{i?26kE3 z1ES~!Qm+wHA4ZTPanHHDwuClIeCSh)uePkQd{l1UP-h1We~LE&3~HU}>acUp8A5)y zu;@+>`J^~&vTN(@xZ}OX?cOzPB*t4~a3IDdu6~VET3x_G#oz-!$c6q!;& zt~z}v71$1KylSj}ZHa;<7)@*tY3ktWkRE=Lkl#7&x7)PEQk=y;Er>NOeMYIp3fKbZ z<^TGyn0BQhJD+iaqc<`=fBr`?KP8$Un>*)A_=dU%INP>ik6g#RIS$(dQi>DOEf-v- zex@!aTH6z;9cgt#7=_>+7zw9oj>jd(3Be_+E;P}cmMIgz>fdh~7j>)sghMEa0$vMJ zu@4CXv;OP&1f@NdQ*_2?^S(c+nH<-64dDD^f4KmI+;sq$2Vt_Db7|5)3o6cXbBQ@H zG1cLun_s(OHC5TvxF%cku*xXK&;+&kyby!T zvafKn!_!LzS16;TK6*MeERHm%N!+z3>DgGQX?c!IDdTHcm|UT=lKL-oq+H$w4Ikj- zP&-J;Ao3pjA)e(uhBU~9ZlNrhbFF%Q^0x3R&YM*mj~N@pidy86w;T;jAXEV-;ZWvK zb2B-YqutII?nj{pAEG>i#p9eYGm;K^QMHUwci4HyvNkf4vy{bstkkr0J%}%-d>f4( z>*$Q548es`C2DinV^HGXciZAQ<;QAY?Ww9M%czAt$V3nmkV&6H+k|Ud88k%lzdaJH zsx6<9=?7Kfqekf$Nd0Q;R}K#QQBc3+W3PNG`?|57AEgdgNRVA1pVdf}R$h;> z4kZ)OfJ|w%tr?+8dVKf(j`FSXZg>o^PqyAWcyul^Jz^aoza}JUy`qp*kP3>pyifMh zx7(;(fMcl8;Q*h?+zqiUmTTPp3q54TG$Fi?NY@f}$P0|?Q44!HQ}8szR|BtiC(HhP zjVQu_2YojvMFXl-p+HQ5U|X`QR{lu2)uL;;+i~E$ar$RSZTNW83WLEK=Ckqug})plRKIlDuvDpdyXa~tK|BbCPOf;W~@TAb;N^xFR!trh1)`2?#cYVm8`;WGMzF8 Date: Tue, 31 Oct 2023 06:16:12 -0300 Subject: [PATCH 0139/1037] Fix relative mouse mode for DRM (#3492) --- src/platforms/rcore_drm.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 82701e310..9212d7e0c 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -121,6 +121,7 @@ typedef struct { Vector2 eventWheelMove; // Registers the event mouse wheel variation // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab + bool cursorRelative; // Relative cursor mode // Gamepad data pthread_t gamepadThreadId; // Gamepad reading thread id @@ -400,6 +401,7 @@ void EnableCursor(void) // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + platform.cursorRelative = false; CORE.Input.Mouse.cursorHidden = false; } @@ -409,6 +411,7 @@ void DisableCursor(void) // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + platform.cursorRelative = true; CORE.Input.Mouse.cursorHidden = true; } @@ -522,6 +525,13 @@ void PollInputEvents(void) PollKeyboardEvents(); + // Register previous mouse position + if (platform.cursorRelative) + { + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; + } + // Register previous mouse states CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove; @@ -1535,8 +1545,16 @@ static void *EventThread(void *arg) { if (event.code == REL_X) { - CORE.Input.Mouse.currentPosition.x += event.value; - CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + if (platform.cursorRelative) + { + CORE.Input.Mouse.currentPosition.x -= event.value; + CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + } + else + { + CORE.Input.Mouse.currentPosition.x += event.value; + CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + } touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; @@ -1544,8 +1562,16 @@ static void *EventThread(void *arg) if (event.code == REL_Y) { - CORE.Input.Mouse.currentPosition.y += event.value; - CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + if (platform.cursorRelative) + { + CORE.Input.Mouse.currentPosition.y -= event.value; + CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + } + else + { + CORE.Input.Mouse.currentPosition.y += event.value; + CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + } touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; From 0d186a0557a3c74d34ddce1daa7a66ae0fc1e699 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 11:32:07 +0100 Subject: [PATCH 0140/1037] REVIEWED: `LoadModel()`, removed cube fallback mechanism #3459 --- src/rmodels.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 0a997f858..b0120932a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1052,26 +1052,16 @@ Model LoadModel(const char *fileName) // Make sure model transform is set to identity matrix! model.transform = MatrixIdentity(); - if (model.meshCount == 0) + if ((model.meshCount != 0) && (model.meshes != NULL)) { - model.meshCount = 1; - model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); -#if defined(SUPPORT_MESH_GENERATION) - TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data, default to cube mesh", fileName); - model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); -#else - TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data", fileName); -#endif - } - else - { - // Upload vertex data to GPU (static mesh) + // Upload vertex data to GPU (static meshes) for (int i = 0; i < model.meshCount; i++) UploadMesh(&model.meshes[i], false); } + else TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load model mesh(es) data", fileName); if (model.materialCount == 0) { - TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load material data, default to white material", fileName); + TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load model material data, default to white material", fileName); model.materialCount = 1; model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); From 68420127485990746eab874a4f9d7b08e5acfd13 Mon Sep 17 00:00:00 2001 From: veins1 <19636663+veins1@users.noreply.github.com> Date: Tue, 31 Oct 2023 19:48:24 +0500 Subject: [PATCH 0141/1037] Fix QOA seeking (#3494) --- src/raudio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index dcc9f706a..476676049 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1797,7 +1797,9 @@ void SeekMusicStream(Music music, float position) case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; #endif #if defined(SUPPORT_FILEFORMAT_QOA) - case MUSIC_AUDIO_QOA: qoaplay_seek_frame((qoaplay_desc *)music.ctxData, positionInFrames); break; + //qoaplay_seek_frame seeks to QOA frame, not PCM frame, therefore we need to compute QOA frame number and change positionInFrames + case MUSIC_AUDIO_QOA: { int qoaFrame = positionInFrames/QOA_FRAME_LEN; qoaplay_seek_frame((qoaplay_desc*)music.ctxData, qoaFrame); + positionInFrames = ((qoaplay_desc*)music.ctxData)->sample_position; break; } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; From 0a3567439d90bb1a4955b13441f157ec9c956998 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 15:49:42 +0100 Subject: [PATCH 0142/1037] Comments tweaks --- src/platforms/rcore_android.c | 4 ++-- src/platforms/rcore_desktop.c | 5 ++--- src/platforms/rcore_desktop_sdl.c | 4 ++-- src/platforms/rcore_drm.c | 5 +++-- src/platforms/rcore_template.c | 4 ++-- src/platforms/rcore_web.c | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 83450bb0a..5d6f7d5ba 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -21,8 +21,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* Android NDK - Provides C API to access Android functionality -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - Android NDK: Provides C API to access Android functionality +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 08b329e85..267262c99 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -24,8 +24,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* rglfw - Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - rglfw: Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng @@ -1766,7 +1766,6 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int // Gesture data is sent to gestures-system for processing ProcessGestureEvent(gestureEvent); - #endif } diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 8793e6c77..7245b2d5c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -24,8 +24,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - SDL 2 (main library) -* - Dependency 02 +* - SDL 2 (main library): Windowing and inputs management +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 9212d7e0c..632686767 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -3,7 +3,7 @@ * rcore_drm - Functions to manage window, graphics device and inputs * * PLATFORM: DRM -* - Raspberry Pi 0-5 +* - Raspberry Pi 0-5 (native mode) * - Linux native mode (KMS driver) * * LIMITATIONS: @@ -23,7 +23,8 @@ * running processes orblocking the device if not restored properly. Use with care. * * DEPENDENCIES: -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - DRM and GLM: System libraries for display initialization and configuration +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 1cebfa798..9a815ec4e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -21,8 +21,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - Dependency 01 -* - Dependency 02 +* - +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index d797d99d7..959f60332 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -20,8 +20,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* emscripten - Allow interaction between browser API and C -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - emscripten: Allow interaction between browser API and C +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng From f88604e6d5e0b17280b05081450a0e9b31d8621d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 15:54:38 +0100 Subject: [PATCH 0143/1037] Reviewed QOA seek PR --- src/external/qoaplay.c | 4 ++-- src/raudio.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index 7f937f4fa..039e27974 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -36,7 +36,7 @@ // QOA streaming data descriptor typedef struct { qoa_desc info; // QOA descriptor data - + FILE *file; // QOA file to read, if NULL, using memory buffer -> file_data unsigned char *file_data; // QOA file data on memory unsigned int file_data_size; // QOA file data on memory size @@ -107,7 +107,7 @@ qoaplay_desc *qoaplay_open(const char *path) unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2; qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size); memset(qoa_ctx, 0, sizeof(qoaplay_desc)); - + qoa_ctx->file = file; qoa_ctx->file_data = NULL; qoa_ctx->file_data_size = 0; diff --git a/src/raudio.c b/src/raudio.c index 476676049..e38e51e6c 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1797,9 +1797,14 @@ void SeekMusicStream(Music music, float position) case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; #endif #if defined(SUPPORT_FILEFORMAT_QOA) - //qoaplay_seek_frame seeks to QOA frame, not PCM frame, therefore we need to compute QOA frame number and change positionInFrames - case MUSIC_AUDIO_QOA: { int qoaFrame = positionInFrames/QOA_FRAME_LEN; qoaplay_seek_frame((qoaplay_desc*)music.ctxData, qoaFrame); - positionInFrames = ((qoaplay_desc*)music.ctxData)->sample_position; break; } + case MUSIC_AUDIO_QOA: + { + int qoaFrame = positionInFrames/QOA_FRAME_LEN; + qoaplay_seek_frame((qoaplay_desc *)music.ctxData, qoaFrame); // Seeks to QOA frame, not PCM frame + + // We need to compute QOA frame number and update positionInFrames + positionInFrames = ((qoaplay_desc *)music.ctxData)->sample_position; + } break; #endif #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; From de7beef05d56a23c4ab06202013965e3da014ef3 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 15:54:52 +0100 Subject: [PATCH 0144/1037] Remove trailing spaces --- src/platforms/rcore_desktop.c | 2 +- src/platforms/rcore_template.c | 2 +- src/rcore.c | 8 ++++---- src/rlgl.h | 4 ++-- src/rmodels.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 267262c99..3d38dc898 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -755,7 +755,7 @@ int GetCurrentMonitor(void) // to try to detect the "current monitor" for that window, note that // this is probably an overengineered solution for a very side case // trying to match SDL behaviour - + int closestDist = 0x7FFFFFFF; // Window center position diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 9a815ec4e..5d4721c84 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -21,7 +21,7 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - +* - * - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * diff --git a/src/rcore.c b/src/rcore.c index 333fa4bfa..175d68611 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1681,17 +1681,17 @@ void SetRandomSeed(unsigned int seed) int GetRandomValue(int min, int max) { int value = 0; - + if (min > max) { int tmp = max; max = min; min = tmp; } - + #if defined(SUPPORT_RPRAND_GENERATOR) value = rprand_get_value(min, max); -#else +#else // WARNING: Ranges higher than RAND_MAX will return invalid results // More specifically, if (max - min) > INT_MAX there will be an overflow, // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold @@ -1699,7 +1699,7 @@ int GetRandomValue(int min, int max) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); } - + value = (rand()%(abs(max - min) + 1) + min); #endif return value; diff --git a/src/rlgl.h b/src/rlgl.h index 707555dd5..fcb8feeb6 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -633,7 +633,7 @@ RLAPI void rlDisableScissorTest(void); // Disable scissor test RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test RLAPI void rlEnableWireMode(void); // Enable wire mode RLAPI void rlEnablePointMode(void); // Enable point mode -RLAPI void rlDisableWireMode(void); // Disable wire mode ( and point ) maybe rename +RLAPI void rlDisableWireMode(void); // Disable wire mode ( and point ) maybe rename RLAPI void rlSetLineWidth(float width); // Set the line drawing width RLAPI float rlGetLineWidth(void); // Get the line drawing width RLAPI void rlEnableSmoothLines(void); // Enable line aliasing @@ -1823,7 +1823,7 @@ void rlEnablePointMode(void) #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) // NOTE: glPolygonMode() not available on OpenGL ES glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); - glEnable(GL_PROGRAM_POINT_SIZE); + glEnable(GL_PROGRAM_POINT_SIZE); #endif } // Disable wire mode diff --git a/src/rmodels.c b/src/rmodels.c index b0120932a..68c2d75bc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1160,7 +1160,7 @@ BoundingBox GetModelBoundingBox(Model model) bounds.max = temp; } } - + // Apply model.transform to bounding box // WARNING: Current BoundingBox structure design does not support rotation transformations, // in those cases is up to the user to calculate the proper box bounds (8 vertices transformed) From 3645244f9fc1874e72252138ca4ed0ab849a2fd9 Mon Sep 17 00:00:00 2001 From: Justin <72092018+27justin@users.noreply.github.com> Date: Tue, 31 Oct 2023 20:13:12 +0100 Subject: [PATCH 0145/1037] examples/shaders: Add an example for deferred shading (#3496) * add example for deferred rendering/shading * adapt convention --------- Co-authored-by: 27justin --- examples/Makefile | 3 +- examples/Makefile.Web | 3 +- examples/README.md | 9 +- .../shaders/glsl330/deferred_shading.fs | 55 +++ .../shaders/glsl330/deferred_shading.vs | 11 + .../resources/shaders/glsl330/gbuffer.fs | 22 ++ .../resources/shaders/glsl330/gbuffer.vs | 24 ++ examples/shaders/shaders_deferred_render.c | 321 ++++++++++++++++++ examples/shaders/shaders_deferred_render.png | Bin 0 -> 90692 bytes 9 files changed, 442 insertions(+), 6 deletions(-) create mode 100644 examples/shaders/resources/shaders/glsl330/deferred_shading.fs create mode 100644 examples/shaders/resources/shaders/glsl330/deferred_shading.vs create mode 100644 examples/shaders/resources/shaders/glsl330/gbuffer.fs create mode 100644 examples/shaders/resources/shaders/glsl330/gbuffer.vs create mode 100644 examples/shaders/shaders_deferred_render.c create mode 100644 examples/shaders/shaders_deferred_render.png diff --git a/examples/Makefile b/examples/Makefile index 5af5a5590..fe0ee9fbd 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -562,7 +562,8 @@ SHADERS = \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ - shaders/shaders_hybrid_render + shaders/shaders_hybrid_render \ + shaders/shaders_deferred_render AUDIO = \ audio/audio_module_playing \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 9c2cadc37..c57453ab3 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -468,7 +468,8 @@ SHADERS = \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ - shaders/shaders_hybrid_render + shaders/shaders_hybrid_render \ + shaders/shaders_deferred_render AUDIO = \ audio/audio_module_playing \ diff --git a/examples/README.md b/examples/README.md index 82e4b7825..0fed8acd0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -176,6 +176,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete | 114 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) | | 115 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | | 116 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | +| 117 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | ### category: audio @@ -183,10 +184,10 @@ Examples using raylib audio functionality, including sound/music loading and pla | ## | example | image | difficulty
level | version
created | last version
updated | original
developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 117 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 118 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 119 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | -| 120 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | +| 118 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 119 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 120 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | +| 121 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | ### category: others diff --git a/examples/shaders/resources/shaders/glsl330/deferred_shading.fs b/examples/shaders/resources/shaders/glsl330/deferred_shading.fs new file mode 100644 index 000000000..c9c6a313f --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/deferred_shading.fs @@ -0,0 +1,55 @@ +#version 330 core +out vec4 finalColor; + +in vec2 texCoord; +in vec2 texCoord2; + +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D gAlbedoSpec; + +struct Light { + int enabled; + int type; // Unused in this demo. + vec3 position; + vec3 target; // Unused in this demo. + vec4 color; +}; + +const int NR_LIGHTS = 4; +uniform Light lights[NR_LIGHTS]; +uniform vec3 viewPosition; + +const float QUADRATIC = 0.032; +const float LINEAR = 0.09; + +void main() { + vec3 fragPosition = texture(gPosition, texCoord).rgb; + vec3 normal = texture(gNormal, texCoord).rgb; + vec3 albedo = texture(gAlbedoSpec, texCoord).rgb; + float specular = texture(gAlbedoSpec, texCoord).a; + + vec3 ambient = albedo * vec3(0.1f); + vec3 viewDirection = normalize(viewPosition - fragPosition); + + for(int i = 0; i < NR_LIGHTS; ++i) + { + if(lights[i].enabled == 0) continue; + vec3 lightDirection = lights[i].position - fragPosition; + vec3 diffuse = max(dot(normal, lightDirection), 0.0) * albedo * lights[i].color.xyz; + + vec3 halfwayDirection = normalize(lightDirection + viewDirection); + float spec = pow(max(dot(normal, halfwayDirection), 0.0), 32.0); + vec3 specular = specular * spec * lights[i].color.xyz; + + // Attenuation + float distance = length(lights[i].position - fragPosition); + float attenuation = 1.0 / (1.0 + LINEAR * distance + QUADRATIC * distance * distance); + diffuse *= attenuation; + specular *= attenuation; + ambient += diffuse + specular; + } + + finalColor = vec4(ambient, 1.0); +} + diff --git a/examples/shaders/resources/shaders/glsl330/deferred_shading.vs b/examples/shaders/resources/shaders/glsl330/deferred_shading.vs new file mode 100644 index 000000000..f2b1bd7c4 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/deferred_shading.vs @@ -0,0 +1,11 @@ +#version 330 core + +layout (location = 0) in vec3 vertexPosition; +layout (location = 1) in vec2 vertexTexCoord; + +out vec2 texCoord; + +void main() { + gl_Position = vec4(vertexPosition, 1.0); + texCoord = vertexTexCoord; +} diff --git a/examples/shaders/resources/shaders/glsl330/gbuffer.fs b/examples/shaders/resources/shaders/glsl330/gbuffer.fs new file mode 100644 index 000000000..c86e20a9e --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/gbuffer.fs @@ -0,0 +1,22 @@ +#version 330 core +layout (location = 0) out vec3 gPosition; +layout (location = 1) out vec3 gNormal; +layout (location = 2) out vec4 gAlbedoSpec; + +in vec3 fragPosition; +in vec2 fragTexCoord; +in vec3 fragNormal; + +uniform sampler2D diffuseTexture; +uniform sampler2D specularTexture; + +void main() { + // store the fragment position vector in the first gbuffer texture + gPosition = fragPosition; + // also store the per-fragment normals into the gbuffer + gNormal = normalize(fragNormal); + // and the diffuse per-fragment color + gAlbedoSpec.rgb = texture(diffuseTexture, fragTexCoord).rgb; + // store specular intensity in gAlbedoSpec's alpha component + gAlbedoSpec.a = texture(specularTexture, fragTexCoord).r; +} diff --git a/examples/shaders/resources/shaders/glsl330/gbuffer.vs b/examples/shaders/resources/shaders/glsl330/gbuffer.vs new file mode 100644 index 000000000..7d264ba64 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/gbuffer.vs @@ -0,0 +1,24 @@ +#version 330 core +layout (location = 0) in vec3 vertexPosition; +layout (location = 1) in vec2 vertexTexCoord; +layout (location = 2) in vec3 vertexNormal; + +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec3 fragNormal; + +uniform mat4 matModel; +uniform mat4 matView; +uniform mat4 matProjection; + +void main() +{ + vec4 worldPos = matModel * vec4(vertexPosition, 1.0); + fragPosition = worldPos.xyz; + fragTexCoord = vertexTexCoord; + + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + fragNormal = normalMatrix * vertexNormal; + + gl_Position = matProjection * matView * worldPos; +} diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c new file mode 100644 index 000000000..58d4b1dd9 --- /dev/null +++ b/examples/shaders/shaders_deferred_render.c @@ -0,0 +1,321 @@ +/******************************************************************************************* +* +* raylib [shaders] example - deferred rendering +* +* NOTE: This example requires raylib OpenGL 3.3 or ES 3 versions. +* +* Example originally created with raylib 4.5, last time updated with raylib 4.5 +* +* Example contributed by Justin Andreas Lacoste (@27justin) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2023 Justin Andreas Lacoste (@27justin) +* +********************************************************************************************/ + +#include +#include + +#include "raylib.h" +#include "rlgl.h" + +#include "raymath.h" + +#define RLIGHTS_IMPLEMENTATION +#include "rlights.h" + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +typedef struct { + unsigned int framebuffer; + + unsigned int positionTexture; + unsigned int normalTexture; + unsigned int albedoSpecTexture; + + unsigned int depthRenderbuffer; +} GBuffer; + +int main(void) { + // Initialization + // ------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - deferred render"); + + Camera camera = { 0 }; + camera.position = (Vector3){ 5.0f, 4.0f, 5.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 60.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + // Load plane model from a generated mesh + Model model = LoadModelFromMesh(GenMeshPlane(10.0f, 10.0f, 3, 3)); + Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 2.0f, 2.0f)); + + // Load geometry buffer (G-buffer) shader and deferred shader + Shader gbufferShader = LoadShader("resources/shaders/glsl330/gbuffer.vs", + "resources/shaders/glsl330/gbuffer.fs"); + + Shader deferredShader = LoadShader("resources/shaders/glsl330/deferred_shading.vs", + "resources/shaders/glsl330/deferred_shading.fs"); + deferredShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(deferredShader, "viewPosition"); + + // Initialize the G-buffer + GBuffer gBuffer = { 0 }; + gBuffer.framebuffer = rlLoadFramebuffer(screenWidth, screenHeight); + + if(!gBuffer.framebuffer) + { + TraceLog(LOG_WARNING, "Failed to create framebuffer"); + exit(1); + } + rlEnableFramebuffer(gBuffer.framebuffer); + + // Since we are storing position and normal data in these textures, + // we need to use a floating point format. + gBuffer.positionTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, 1); + + gBuffer.normalTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, 1); + // Albedo (diffuse color) and specular strength can be combined into one texture. + // The color in RGB, and the specular strength in the alpha channel. + gBuffer.albedoSpecTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1); + + // Activate the draw buffers for our framebuffer + rlActiveDrawBuffers(3); + + // Now we attach our textures to the framebuffer. + rlFramebufferAttach(gBuffer.framebuffer, gBuffer.positionTexture, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0); + rlFramebufferAttach(gBuffer.framebuffer, gBuffer.normalTexture, RL_ATTACHMENT_COLOR_CHANNEL1, RL_ATTACHMENT_TEXTURE2D, 0); + rlFramebufferAttach(gBuffer.framebuffer, gBuffer.albedoSpecTexture, RL_ATTACHMENT_COLOR_CHANNEL2, RL_ATTACHMENT_TEXTURE2D, 0); + + // Finally we attach the depth buffer. + gBuffer.depthRenderbuffer = rlLoadTextureDepth(screenWidth, screenHeight, true); + rlFramebufferAttach(gBuffer.framebuffer, gBuffer.depthRenderbuffer, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0); + + // Make sure our framebuffer is complete. + // NOTE: rlFramebufferComplete() automatically unbinds the framebuffer, so we don't have + // to rlDisableFramebuffer() here. + if(rlFramebufferComplete(gBuffer.framebuffer) != true) + { + TraceLog(LOG_WARNING, "Framebuffer is not complete"); + exit(1); + } + + // Now we initialize the sampler2D uniform's in the deferred shader. + // We do this by setting the uniform's value to the color channel slot we earlier + // bound our textures to. + rlEnableShader(deferredShader.id); + + rlSetUniformSampler(rlGetLocationUniform(deferredShader.id, "gPosition"), 0); + rlSetUniformSampler(rlGetLocationUniform(deferredShader.id, "gNormal"), 1); + rlSetUniformSampler(rlGetLocationUniform(deferredShader.id, "gAlbedoSpec"), 2); + + rlDisableShader(); + + // Assign out lighting shader to model + model.materials[0].shader = gbufferShader; + cube.materials[0].shader = gbufferShader; + + // Create lights + //-------------------------------------------------------------------------------------- + Light lights[MAX_LIGHTS] = { 0 }; + lights[0] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, -2 }, Vector3Zero(), YELLOW, deferredShader); + lights[1] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, 2 }, Vector3Zero(), RED, deferredShader); + lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, deferredShader); + lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, deferredShader); + + const int MAX_CUBES = 30; + const float CUBE_SCALE = 0.25; + Vector3 cubePositions[MAX_CUBES]; + float cubeRotations[MAX_CUBES]; + for(int i = 0; i < MAX_CUBES; i++) + { + cubePositions[i] = (Vector3) { + .x = (float)(rand() % 10) - 5, + .y = (float)(rand() % 5), + .z = (float)(rand() % 10) - 5, + }; + cubeRotations[i] = (float)(rand() % 360); + } + + enum { + POSITION, + NORMAL, + ALBEDO, + DEFERRED_SHADING + } activeTexture = DEFERRED_SHADING; + + rlEnableDepthTest(); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_ORBITAL); + + // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f }) + float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; + SetShaderValue(deferredShader, deferredShader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3); + + // Check key inputs to enable/disable lights + if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; } + if (IsKeyPressed(KEY_R)) { lights[1].enabled = !lights[1].enabled; } + if (IsKeyPressed(KEY_G)) { lights[2].enabled = !lights[2].enabled; } + if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; } + + // Check key inputs to switch between G-buffer textures + if(IsKeyPressed(KEY_ONE)) activeTexture = POSITION; + if(IsKeyPressed(KEY_TWO)) activeTexture = NORMAL; + if(IsKeyPressed(KEY_THREE)) activeTexture = ALBEDO; + if(IsKeyPressed(KEY_FOUR)) activeTexture = DEFERRED_SHADING; + + + // Update light values (actually, only enable/disable them) + for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(deferredShader, lights[i]); + //---------------------------------------------------------------------------------- + + // Draw + // --------------------------------------------------------------------------------- + BeginDrawing(); + // Draw to the geometry buffer by first activating it. + rlEnableFramebuffer(gBuffer.framebuffer); + rlClearScreenBuffers(); // Clear color & depth buffer + + rlDisableColorBlend(); + BeginMode3D(camera); + // NOTE: + // We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` + // will not work, as they won't immediately load the shader program. + rlEnableShader(gbufferShader.id); + // When drawing a model here, make sure that the material's shaders + // are set to the gbuffer shader! + DrawModel(model, Vector3Zero(), 1.0f, WHITE); + DrawModel(cube, (Vector3) { 0.0, 1.0f, 0.0 }, 1.0f, WHITE); + + for(int i = 0; i < MAX_CUBES; i++) + { + Vector3 position = cubePositions[i]; + + DrawModelEx(cube, position, (Vector3) { 1, 1, 1 }, cubeRotations[i], (Vector3) { CUBE_SCALE, CUBE_SCALE, CUBE_SCALE }, WHITE); + } + + rlDisableShader(); + EndMode3D(); + rlEnableColorBlend(); + + // Go back to the default framebuffer (0) and draw our deferred shading. + rlDisableFramebuffer(); + rlClearScreenBuffers(); // Clear color & depth buffer + + switch(activeTexture) + { + case DEFERRED_SHADING: + BeginMode3D(camera); + rlDisableColorBlend(); + rlEnableShader(deferredShader.id); + // Activate our g-buffer textures + // These will now be bound to the sampler2D uniforms `gPosition`, `gNormal`, + // and `gAlbedoSpec` + rlActiveTextureSlot(0); + rlEnableTexture(gBuffer.positionTexture); + rlActiveTextureSlot(1); + rlEnableTexture(gBuffer.normalTexture); + rlActiveTextureSlot(2); + rlEnableTexture(gBuffer.albedoSpecTexture); + + // Finally, we draw a fullscreen quad to our default framebuffer + // This will now be shaded using our deferred shader + rlLoadDrawQuad(); + rlDisableShader(); + rlEnableColorBlend(); + EndMode3D(); + + // As a last step, we now copy over the depth buffer from our g-buffer to the + // default framebuffer. + glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer.framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + rlDisableFramebuffer(); + + // Since our shader is now done and disabled, we can draw our lights in default + // forward rendering + BeginMode3D(camera); + rlEnableShader(rlGetShaderIdDefault()); + for(int i = 0; i < MAX_LIGHTS; i++) + { + if(lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color); + else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f)); + } + rlDisableShader(); + EndMode3D(); + DrawText("FINAL RESULT", 10, screenHeight - 30, 20, DARKGREEN); + break; + case POSITION: + DrawTextureRec((Texture2D) { + .id = gBuffer.positionTexture, + .width = screenWidth, + .height = screenHeight, + }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); + DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); + break; + case NORMAL: + DrawTextureRec((Texture2D) { + .id = gBuffer.normalTexture, + .width = screenWidth, + .height = screenHeight, + }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); + DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); + break; + + case ALBEDO: + DrawTextureRec((Texture2D) { + .id = gBuffer.albedoSpecTexture, + .width = screenWidth, + .height = screenHeight, + }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); + DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); + break; + } + + DrawFPS(10, 10); + + DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, DARKGRAY); + DrawText("Use keys [1]-[4] to switch between G-buffer textures", 10, 70, 20, DARKGRAY); + EndDrawing(); + // ----------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModel(model); // Unload the models + UnloadModel(cube); + + UnloadShader(deferredShader); // Unload shaders + UnloadShader(gbufferShader); + + // Unload geometry buffer and all attached textures + rlUnloadFramebuffer(gBuffer.framebuffer); + rlUnloadTexture(gBuffer.positionTexture); + rlUnloadTexture(gBuffer.normalTexture); + rlUnloadTexture(gBuffer.albedoSpecTexture); + rlUnloadTexture(gBuffer.depthRenderbuffer); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + diff --git a/examples/shaders/shaders_deferred_render.png b/examples/shaders/shaders_deferred_render.png new file mode 100644 index 0000000000000000000000000000000000000000..44129f0ff0ecb67f96e04d841e6e23c220f21dc8 GIT binary patch literal 90692 zcmeEtbx<8m*XKnN2p%jza1ZY85`bNck@-;wD)-4m?%P6`!?00{&Fp~^^0D1$(7q#zJX2O=zRCkcix z6S$0lHDyd?WavPMzz={D1|9?hlt|%#;@{;C;07!V90<4t3It9}p!`JhbP~Wk`*RHg z%CG+}#{p&9ziFOYVugVPp#s-5;A8{J*uWJ6oEBgIYO4h(KOKmF{-lA@6X$&eC({GtXzET+-5 zceFFLum*!bZfV}pg3_HLgx&f&dCGJ|d!*z^*?hCz%!F1rA&O*^NTqy9x;M`h2x72b zNwwGf`qgVR)!wb8fuJFZaR+j6aZq-S#(Ye~*6wn%Ux;+QBz8Rn`Bn|6R?)x|rhrY9 zWylisaXyzJ8p6NqVqs)e$;9gB5a^u|^zv#BJ!Lp_@{RDxGhl0@!JtX9)$DrR#h#il z;-1MfFc2f=^-jQc>{&2LJyYXdPyKp%rrFzYYtsxKS%$a<%>yHKF4fgHt4TSQafhSz zCH70JCF;8+-tQke>_v67Hu;7Pzm*431!0ipMj%1TF$&Zux+MZHVu>HfdYNP7s5PF2 z5h53gep$b2fBfZIXX@bCp@2l=xAbcM0q@!8Mv-E}`Q_f)MLrnV(YE;M2^h{n4--_ub@2K5v)f5T4umjit=%c#;&LizGD2E`t!h zwrK`e&Im3o&MeNin$6wcgPuh=uG|~d9RW7zW}&L-tf?T+XKZJ~WN2b%1ZHxxv42t+ zNI=-l-q6?z>`ZC|HnXr5BtL9yB`38o5hT~(RA5oC7YCbLNP9SfRXpCQ8hcn7^O}$g z3n2-(@c|5Mz|MxGZZ_7oPJC{Hu~$1DbJBmvXJfCR@K5%( zPJg2S;KA%>XwS^b#KLT2!~E|xoSY>;0!;oE=>J&5N!8sR%&ZJ{vU71X21|Yf+d5PH zI|~!zf7Z8makT!^9TQ__ur=5QAaw#-W&MvPpFHlLHJ%_av#_!MQwsq5KPa6oO#cPe zf5`Uq(f&otzDAjloZ>0Om{<03TCU zUUow^Zc|2fLk>1Z4ij!(Mk6jT3!{;tA&)5w50{A{kMX~;c<*QdSf!!$zf1MR$^>A= z!OqId%E8UaXw1#Z#mHgIW6H>5%x27JWXj9S!OF$T!fj&ohn0yj-&;FJ8$&>y7B+@v zU}k$;vp)}>0Ou1`lo2FnV`BN&9Yt$HXH%epAi2DSt&7{g9;jN_fK{9gpU`CG;^pDy z0uEL{XdX`Pe-WvJ9i0G6eB${hcK&(tq!vD)GXSuLPj(70_;VlV3!k_n*wER|QPs}Q zT9EvoQTLyZ6##cKF?2SRFmwh3O#c~&RsRu(=~-C$SpE|KiJgL-iG`{A|Bd>|c}N8S zdO+pU7EVC>?tgCmqP_}g6nao^(qU?!R0e?0!y#s9|^0M`F)m2k{zt_BwXXld^*=)3e?-v8N7t+6`Q(#+Q6yyp_mTH;%9Ds?|b0aw^2@n?O zC5Xm!pYCa*{lZ>a%LxQR#(FwoKq+Z>z)b{a83jp%EhG|jDw@ulJ8=+*6eJ@ds_Hhk z*X-&=I91s7-8b zOzm>QZ(Wzo+%N2ya^S@snR~DY^XF9I^7~?RDUC}%7NlK9&ZYxLa=K0B!0G)FYw>KA zndwMc^7Zhc`v^YhGb!T#vwzw*zt5@QlE~_DUxN76KqTO^rk%^ygge!u zYBlQx1L(KL3K3@=Tb0u+lJL1_T$-Nd-+Uy$C}I6+P89o>I`L^;X`iF})1`9`x%qBBx!d&mQo<%;!X!SR!QNT%He-0ph;vXg}P(Xi#R+pT6 zQJL^Va2J7O!e>((v#P%)TJQ($RudEoEwihODn83z1bwT1vJRsmM26KYLd-6iSZcDI zUxpw1-ZWh5FDL^}l#{^|cci_EK2r=iyqg{&<8bPSFO))$lr^HIyR`I@39OdisZV?g>iZT_^i{bl z>dA86Qe%@7;(u-D4WWHk2j8 z&-a#2!5FpT0iD2<@JE>??57fjKT7tKr0nZ$VT&L<$wQTT*zU-5X+CyDbjXB!PQGi| z67Tw?h*0GI)Yj^+h2^3bA#(2S_1D*JesHrcG2o*tN|_!6vH%DqLQWo6{=?XH?1(*W zi6tb*qWGZoe75;E?3L#0{=@sxn$Vv@y9w(JHcPi^3v+*>0X*&s_Vy=zbJszb0mQ?})Caj-B)GeREZWC=rIZ zS_<>c1dzzP`ORki?$Yo-hQittb;pBhdntTq7Yfxg;P_4y)Q30WBDkACVMK`;4aNtR z9K1T%AkXYkH8WZK~@{;x>!T&vG(s zBqG-Zhb2#KX`4>>1*vzDbO#A0(+O7hJ_C`^(`Z0>S#6a!ti*Nl04N72sPhd-y<>#L zI<`MG0$-Tis6il>DG6Gs`4)lQ@wAa8e7gIE(3`I}FC}R4toe68=R&MZzYK=cvN#sq z6qb>$CC{7oac+%7FR*kL;}H-HG!#6W=1S}JUOy}BsO!=e2jF?VMFDuq5rq;KA)(=* z=2B}ZirSLRpew>yJ$%nRVHG1a4fr9$5Tzd>SCkD^W z%Z^nhzEqZWM|Q@^I9O5K1(Ee=Qewh<(rf$(#$~w^%d#g1nxMteT)O5?UK?2$Sg-r= zc;2`Xv9A4|%*1!!pw<&I1P+$t@&kLt^HuA@!o$P2@ve+DinD@5daP0(a+O~#j^5F9 zd4|QPzZwYPyc~4V2cbp#gN~FOgLQ1aU?6=VoNqm%P?H|c+dUQX1v^u&eMcs{EByF4 zZ)}4v3=Z+s1uR+GfC^eb{0|ve=az?_9jmk|J!f;2ax5)~;t#+lWElG1mu>Ch*3nSK z#M`*!8pN4mSik|JvxwzFgBvaH1(15ec^%Oe@I z9Lk!6B_AZ#+~1$^9fmW1Gkv^gv`Q8j$!~8L?fhQ(_Blm#kSp}t<9nq3$-|F}bxfC4%EWPyxs&Vo5a`eU5rH~$HCJmBvHIuRzdYuM1gm~3EB*JSj zD&C060d9*;osXXoLBhh;(Uf6BWnKAXpCYb6zc7NHzsc$+c)lJi#l*qo@*_twCEvxx zrF-qz=)9o=J#(~0HIA(wn$F5)G5&36BrHfxO|90jKo@;M-Hw277t; zy7L+9!E7@4WN?HvbCBD1!92o<-spbmRyhbE*1hu-D;VxmpbRSd(%mmfn^))P=VdLf zH99e#YTxN5HpC>7(?rMFPWP*jpC0d;KKFs=?1uVON?g*YBpg9O>COQMo2%C201X62f|DsunEVJuN8bp zB&)nJ^Vutw21{&KmOd9(g%JT4V!kZyP{aC+)l~AVO_B8H$BS#TCC{ycY*9rF5z<6z z?$W)b>ZM7|?i0VXJthu$X03Pf%FKh6lrp{k*V&b#!fm!VwcF8N&T8j5M=KT(oE;1skZq^R$73>^cH-G%6}`mB_-iot>wY*^`i?nK zMff{(yW^(KvNJMPBf7bkJ2<{gurM=T#^H6|@iI>lz=?_P_My@9Yv2+wyoxc-Hzbj< zBDE`(_KEkxL`YxQIMsLJ<7Cz#rKOC7XyPa7azSogFQu%Pns_4};5ywofsq5=A?%>T zTf#aEM_E}@`LUm2x~V&MWRF50$7Ry^o8aVRHicUysoA31He8q@v}lS}NX7 zP0~;6`6Z=BPLb^E-#1PL3Z)kA2`j@!1zepefyg88iNu#{bpd+~m3FO7http{8Jq;f_zc@PAe(>1N)2cGn3a5V!2M3q@6vtto%E*PrZV9e%D9_g;;nw6;!8IFOE{5!BK&BHI9FRHhE0&sX_}tG zijGqnW1j$7nf*Nss4xX$9+I39@0=7i7^Q`mzmoBpuwOG{h7-D;ijWZP+ixK&m6dQ|Jcayp$ z4+&a5x-qj(mX?+Y3|f^R_vhr-2CP}v#9ds@b0kAQ_*{dnRIU~>N(N8)BW2TVWPnkk zV_V7dFcS6`vX787qvq9~im+s&ldH8zGli*>pCczHCu@;u0eB*tvl>Y>#mw=0TU!@lr2y5vQi6To+vu zrQ}uA)FhSO@vH=t>eMCVNZ~XK-)_qhgG)H)$p^z&?JU5xN!MCRahsc)fNI}F+Ke3P zzRpNg00!q_9tiXWNqR~H>o+>PCq{bTk&9?f`&*it7HqVoBMI;-fw3M$e5#LSqR3G& zZMy>*2Ix<|0{Fa`aG-#mF<8!Ol=pC`(U(8>I+a=QTnhcsly+Mij7A&~Caf}toDG2t zRO{$oO2-6Xh#~g+D~Aws-jS0!>21cCa}WeeP|C2y%+5gdcJZ*PYm#c16*2^PXS7a` zg=eJ2o}?>dJ<>WY4x_n()i7)xCSCm|6YG`lC>LWgk||VYYrDmmK0TW~E|2Ui9JXzn zpeBufkjTloG}}_Co>Lr>pMKpqFYI-FSk_cCg~&Xo8^WvnOXDkHa+)I9bt{L(mp6G| z-j48}+{{Vt`x<1JhFNqn=+s)T^@Qg_nMG@@zm?phzASi`J~uhJo-}mu)tqB)e0+R% z7C*B0YxW}+5aNP&={QC6xmB`W8^-L~h1)0P(28M24+JsvVZp?Iv!2Gd&$S3PjakW>i>Kh7J#FT*Q36 zXD1ev%x(tNsbeBU+scN5ND0wgot-(jxX|7|(g5%ICc7`j2`i&${l?Erp2fa$UK+JKsNEXT-y$K-L11HR zyHss4QMfDj<+3h6KOglqmq|E^M;7hCKs-H($8WOEE`rU6_kiCBMfc33p+7Q8OjIu| zeRw3GKb{kvAIj-g?nu(``v%i9Hh<#0*a^$6AMXJ%GN`kMS&Mq!E=0?{;AD>W`HVEeopJFK@le5PN!NxixgOn7e%r!U8M>Y%; z`cJAl&@Oa6u?6U)!Z$4S<6(!gzZvSg!z0|6g-@!J;mscWFz0B^e2oQ+5?qwzfKVFM zMj%B%Kiopsoz(87vRE~z0V{&l;DShIs)Gs(Lex_$WWS<0w04zLDwzmZ{<}ui&kqG_ zV0|3#Ap>u30qCA|NCE1;!eh2L%GSi0#RSHaVC@w zO_%CyO^+zZ5C8h+qN>nbvR`>$aSvi4<1rjgwz@NCP*Y*P?F8R`y?|>A7z^MuD?2V5 zDv7@uvhfuU*8A>)#b+%9E22Ppl>45`(S&Qi`wO%X2@VD{UB`q;oaEPWzB}W|p$%BA zjm`eS!N|G7DBy)%_J|AsZAQ@^(tC{7Ma0!nb<4(XnpMalmEKR@5!-zPqtQ&F3`b6*k6jv@$|LPsv@MZxAnDp}!xHt9FT&TdKF&I+EC8LVYf zy)a@xWazYl0io0M?9b=ua|YVzeaYRuQF$-O$-&|Ls|`l#y;Wx*YPRC7ZE~%-Nv4Nc< zBxOobgDc)(PP?|u$g<>gc4RX&m1aiXwiUYg5V5s>@Oo$9Y9a{F)Tj#{>uZ7>=#x;) zQfVt&P)`n{Mh!L_r#LhrtT!}maB#4=xH$AD7E)2R=6!ja2OHcM1S;yJ?U{!2>lF(% zZmHqh?)lDK$-pfnt_El%h5BQ8%~EGWIXOgxA`9(96n70cI#uW)MksbdqQY2%zM;|8u4-z)286@#DQ_?+S=??ayZtCy6&-DFXr0miqv#cOnvGUG!y5OluWF2Z~f{tm+o2pow zGD(r$BdRwEuZl=?+1!9hv9kuk#DHvH$?kl3e8SxIVdOB%DlJs(NWG>vOOodkGq_JXw7#4 zs5HNiwELP}inLcJq?7gip6-y?2cs+po^_g#;jlpbknO>V)B4hGo}w)CW^v@Yn$=e5 zBJaD2(0+6EX`T&X#x?F6+Kjt8GIw zlaa$iGm4TItc5de;+Llmf7RlMhVlD7JD9~=dLMPYVVU$^?=`nANAE69h0me)n}&vy z5hC7}6*sbg96H#5~vmWfaik#kn7El1QA1g)np2@cJ{|2?0Ll!`lQU-6JKy& z&9e54t*H~S=UxSu*~~3LQtvrL(`?NCgX9aNFys=$HZNUvVK?E@2z($JkYV5dB6>jj z7Ihz___Bod295)i=DwJms=v6GG%qBuzqed8Fq%g@O^j2_c{j-}u+|~#;N`^05>kU| zi!bfd!g&4>_e7_Tp?HW7*{ZopAI)Czh;Za+*nJW3-{cK6$>sDd-P|d5#8s`Hn2?zLuc`VCqHoTI0 z#A%Y7(W^IJ`e?ZMLzY6Vyku1i)XpsZrr;N3AG~h6SLZY+TB+nb3f`2H;94v((t~&UpUH zzmmB-POBz%-p(4~Qx~n6Zw}oq%y|+gDc=XUK^Vq-f>1Xfsf62{d(1!GYG<^ zI|cG9*Y2IO>JQvKe5PXwdKop3=v(#j7ye|8>qB0@9rk` z`?#(~zxH|MS3j`K%vvq%RQHzvlbcQ6;RYx@ggihbFeLw*yY@TvgdDmCsAqMsPAEzO!w7%#U6)r4=B@pD@7_>est;IY1{BX zJ)0eov?%WId?p(9v2tDFR$lw_c&kA6H)eMhb~s|+yW^66T;h4H4&kXro=)g| zOJAke)yh}6HXs(~&?1c$J0f-z_{XaZL21E`_oXiG74PQ6b{srw2TQ7swmj=2gf4wnr{|BO-2{DEMlN3oBjzCgh_ zbWewY=(U2Z|NHlE(5LC?>5L5Gc`9gevDKBn$UBT&af5JH*>rvIYwj(#q7#+;sFUPY zZ4TGymmD0+@YWdNk~&b^ZI9B>cW|FcPqHd1Dspov2+`o$Zq9f2N9`c8kWf-TNo+>; zEbP&C_g0&ktD}YDQBL1FcgOwp3lgfWVKGJ3yz`yuk?{1|B7BT!Hp(ES5Z9T!2t8*! zEWMp>epPVVnbk_?`U3zx?vwb9isF>l56~7DRQq zCW=#G1;*~uO#|g$H5l7tyrhWD7g@&wN9Dr21bNg67Z-~^Eqbw7SX*1$ z*^P#w+_wmOQ$eEJ5s?kQTD_9VQ$Ki#U3+HoEX^WTe(ex0=xYxlG--XyH ztuoLW9xwJMm{{_!L~u_8U00Nc@C+bD>^vZ&SsY3oL?&(Ua?M}WYfQ(K>}#K45K0tG z`L}V*<}$%tdU49CAcsDlw1oi7@;@?Mq8Ub_^W_wS=+2sk+_MZ{uD>}T-R&aHu5J4cd1ueS+)c)gZnBOWMUZP zh%WE75zA>Af9+{1%y`zl78Q@t>PqV7c_qKgjTwA)aS`E89|}m_fiA5ef`|3pEE1hs ztK2};%&g!TmTlhOLd?Q@>!hXm)ARgx4*G_&?Z_l5vkB1D_+WF4C4e;{xRU z1i%q}X`#XW^prz76|b4+If9_c^iSy_HYm;}2@}IV_5$2q?B~J*i-3NTaqHx%QqFBT z)jqkjvQAE?`3k9ptih}$Q0oSGw6|SRa2)U*nv=To4aR78x%hu{uXLs(H+^NGo<|kX z_l8ymgJK7Ug2n~>fD|am8cM;$Sf*WPQXe2GxJaGgz_bgCK{`VZ(~a-~Q?rYQoTfBZ z#Vd-bvWrJpzX_~fgjGebe$Vk_cMgR_Zr-|?qRoe zGNV7A12NIVTI=t~q9*kcY6lR-E70hV3C2Ogi1ue=3$Ey2IT49oi%eSc%@b2wD{DDH zDWZQ8xaBDMPV`vRXhBFjuCHYkBsU0|i{SfUByU=B?^M98K8fv4R4>CoLE)`g^9q>U zllSCT&(&CI*x8j~kQWc-0Nvez{NAx#RMJPJL0G_<>7uKprl^nvR-A%>TztO816bWp z5fDc*j^i96idM$IgWN9z!2p@`%q6C`kh87*vMMR*2>p}wP%=?h!Yrp1k`Aj3HQrV! zv%aAk%8OVchC7;=xZzCi)RtO)Gh6PF6FReyN23J)gy>EjBbpH)HG+F$f%HRKj z)1U(O3t`1Ow4Btq0fn5r2Nf4RlH*vHmtBZ{o9zUHcrug?3~)LE*=3Z*&^sWM5j0BxCGVBQPeMK8SJ%70!6-;Zc|Fd z4$1uF6|$avMcc5{u)?g@omvt~+QYSO5Wbz8Doy_K7>;Dm?Lr76g|c5RLEmd{dq(x$ zhu=2l$uB#%CkmE551YjhC6!L*Q2^m{k4oMMq59b?>*ZOcp^y|GiIBm`w*;{)lV+*3 zXZ-f#;njp+L3b?g>G%I0dk^m!8+V#DJ}3u<%@<-pBmvCSW?%|2e zz-K+d`aq4l$US*DIL5|e!>BjY*wD};<25$7*KT0nA44@NbZgCkn`_HEm%;Ba+ch`; z%9pP#iMmVDt~Dw%J{}Q`Nh<@F@N9gcgASaNGIz4R?1=i3K;x6R_(r4;7dpZqvgAzS z{r*Gvy`YvH+b8^(uurgE@6@v4<&Dx(h$i8U_k$cieE4u+hD*ZO(%QNeo?;OiB@+RQ z98Jd7b6p*nkN?%nb`ojaoGC!Fp|!{?EC1gFwp}^tL)${1CGK! zM@fmbt?k2U=HuPC9P|Fq3geR6Hcd$cH)h<$+PcHIB6j~U#50r- zp7n&rR>fvEur+`sB_-3b1&pU@zOSU=Smb=v3%b>xZ(#q}rlUO^x1Q{GMD>-Z-grSU=~$RnQdY{>y5&^lW^9>R#Xxx>T5ZxQ41^cav5TshJ)Hq&5!lRA?zx!ry# zb33cEpAH zU%H=I2k+hxl{Md;jQJThYLd3WXB?qjSP@w8uNsK*Y8rN=!%xDUs2Srg9Ib&&HQf+Mf{Pj%d;jp$l zlW8ptE*5Wr>#`4d*~hRhza^28zL*T-L*058%N8+TABB1$QnTmJ{4-}SLaf?6z{#e4 z?_6g@GDEA8RAo=xWh}t6GKg$G4`j%PZu@yYYVPmDck>n^U!+lpBpr{ArVL`Zka0EL zevHJV34a>@Af0QRcyxa)stEI9faxeNkvJK*di)gTur8J9T+gbueH!^d$MxOLdXOQL zxuB|u-v8$2Mub$PN;Qm672`eH9v3Gr7M{u7NMLsZ#;c}g7eM4)@+uw?2{bCqdBM-y zBLv8~Pf=pIL1-y<*%X<~!_yNz{X+&LBCVsM9xTilS9oJ~F-Dehj0Q<%6ossLX2#ML zf8VU+SEB3}4!0MHALtm_eb#hJbVahZ{A-{8cu~knQwPV!cbhFv#=X#F|G%FYFo??ka=W>?$+Um z8*2^Isi-D537S-=>rSLjBP}O+6)c?AulK#FD<};=zR2DMOq`-MOzQSa zA0~GiQ~xW%kL#J(gp=LB8q0is3yu8F8f2Do>5k@}^zG<~QOn zJQo+oG!V)3dZ;7}K)}qSE8ydjj)l5gQ_K3lT z(nb%isu(k}a3ts{fop0oPvfXp10(Mo&Fz;JAJL?eIrUy118cJ$52VPl6m*~$iUemly z8V&V3(vsaHAWUkb64#c&Pb7Hdw^7amXu{U_Skjk(9s`l#L(|pps}FIU`&TGcbOpQV zkljbpKmOpzA0k;^25cf2gBA)XFzCQ!TQ!VjdS1P{C$Slx)C_Ek!Az8%BxQZJ#`o;` zGenRRaZq(I-)5_^Jgl>Ae1(iYNLjhn^Hzk4j0+NocrIggs@Ip90{MupCQQgTEDR$ zrg`OLvLHFjWBH~|E{42ITg}%}M?-h`Wb()4s!;gRh0?zTb3Fg-&eWa1(sdcT88fKNc%s1V4g1n$XU zloas$caT7vfdh5|n{KS-^HKiEjsx@(K^BqzjxG`ziY|ATzTO>m?-tIK6AB`XBY7|S zX)-3A&jal;7Ee0{bAHW;{T4%P3;xfwWHJL6^R}qR8PpYsn<`xP@ zG^+^;Z{(03kA=_LH}_EYER^~%T_(x-0o{aphGRm6dA>#1u!?|5Jt^j%td41gNN?BE z)*J|RSbqh`83qf<6it?O+LZVEgakzX z+;RtwGj_mqMEHISJU%G}_WAA3ltggmK+q3pv>eN@vbg*Gqt`W^7T_)9zsA$~5#SL&H8(I>J8}O!pceb4 zpvoAKU6J8?_3Adw=!>m=CyE#kJ*d6toSuK$`;`CH#r|xf*2$Qb3P|Kr(Ba|!+>Ma{ zF>^@4)cHG&U1psbU%O_84cbN3Prfi-Li;O{2^$v|A44!BV8UxSKZ=B)X0&ej+UegV zgL*-Zzz5|lz*ptS(E`udsLiDrrEg_{R}GC+Ljx>Gg!)-0jqhW`NM;82S^I0!00sJ- zd4DXn`i6HYn%?6sbxurvB1|%*A{vhT?@_~Dj3b0_$?40|Ew60ASeDSn6XIYw5Uuly z{t}SGC^?p{@b?FS2(>76-#E#L!Hc|DsZC~w5q)XD?a6=p58$0G2<8yP<; z8odWyWnGFO#!|dVm_~)1(>}xV;Z{wv) zamo%|d74*9m)I~Fh6H;=1Xc2RSKEQ_@|mVLi?h_48En|IPcN|fND-S`4?71Miu#fY zdMYRLcNhH;Rw*9ye!+@-B1Fk_oFYmW7JR?h`hNS6^Q%ZsY?qjXxOVzg=R9vBs-9Y( zT(0=ak&Ey5%lxWJD+U#fc!k3Hiyco&Q~6{+Wyb>)NsU4pEMVRo87lqbsmrJvv2!h6 zrCrJv5}4$q(m{2@DMg45+as5k$Yh|49WkDI^<-biH|6W!4h1idm@mAm!E{?@%vbQ2 z3wuL{m@*p&eU%G8KGx;XcC&?GQcvUTf|<6`N(%X#wqi{QiR1{ZtoDrGd^FgYh@3*` zbmj8N4|56>16eiz0ew7AT~$pdTeLx$MiH z90P9U>gRMh3E06ZYfYD7d#~^!!RPJdG{QHLX#v;No`#E-xSkGqr7wuE1Ju*HPA(lK zF=4KyNYeLm7@e?!%8{G?8n9Ab)n#etJVr?s^QI$BzSAY1ro(C7S_*Rv9OP7xaVAq( z(058^-t7~}n@|{hxgs%VWmR|Cw=yvy=GfimZts2H`_$=QB?@8<24Qk>DbMvL#c1gm zkq8ToT`od>Q7yG~XIv;03$5-Og=MuFpnCI)8tKnQJvard-Ai|eNUG^V$bkl)eO|#4 z{+?@nBhYo`#UN47rOf5|P}J&S&>iW+{Iz3YilJPb;|ai|0bPLMCxQwAxK(?;}|q|h)L)KGMv_mt2zW>j+!qvf8|_GFxP~nr0ZW(i)$rT;!6l)LqG@`9Lk^!n72xQj*<( zbo2|6kwUf*PwTY)Ls{>%rWkzTW)mN8JeT2wrd~xP3CVB%r}&uyLDU2NZa|whk(5)R zYgoEj*MA$`!1g8j7!f=YPKVG*^TA1KcIets&)aR}PQ~Z6O-A@#gL*PW*y-F1@KM8b zSsfid{&BYb)lH36*`v~UQOtUY7Cb!bkYXfZJW8?mhJA5OKLf`@v(2>pCkdvzPZ*wG zSl7o6EGz_$COgzJeTo!9_WK)B+Tv~=x1jv>hjIlFNW+Kf#QvUhx6zh`+yGdSXIJ{4 zz!wz(R=`%H7O!jeQ33Xy$thJ{w}CWw(9u`~x3HqoIvS6g0O^x*1|}k>0SDsrsSOi< zJcy*TDlnlo4o~U&SO8@#u}FYjH@Nz?>x&MC1uZ(3BJm-t9Run9UP~$&ShTE-%5!+u zG$BOJ_Cf*c&SdY6t#E9a@ObI*6wcF(S#~q04}+SF#whTxSs11{Z{rXi>{jR)HI4O< z8z%Sk_1=Vx&&|zEO{sO6vgQO_>*liO1pF{}o`R?W`;zeUc4UE^(_S3!Y5ud%oF?FU z^Ac!wB+237?ZxJkU&x2+#8kvAiaa^`V4<|NtD=Ss7QG7-;xFCRm?0;&s4jaO)~EcG z&dgwDuS^A-$rB$Yj!k*KMRi-1W0)n=*AkqQ2N6)Tg z%X0O^0k(XrzRZk_j8(J=xlqha52BfgNi{h+IWx1Z!q<|T-Syefur+elk2h)8^smv< zsS#w1>-$T35pG)U&M20>9x@SD^LxZp^uk+yW&Ifb0yU?j1Sd3a-Ogsetel{1dK$%? zveNet<-|^VU#FB4(ix@aXS?i{Xt|DNaE6`|u!{o&RC%A(K0BfMD_us-*bB7n=m1_a z^^sTRfUUw&me%|zgkof+$uZ~+vr7b5sB8_T;v!a5zB%h*lnSqL|GnmWcIj<(DvaK> z)_5y~Ax1f=Hd?5v0V?09f!a|bV1}ek#^W)?Q%Eble9Dd4x#d-qVYavL)#R*k@y5%( zcEK~R?^gbh%=CrEC7EZZvdOeeiCxBeB2Tmr+1PA-_2?fGl8}O{Pxj zg&@foj)D@0Wbm6GQn`!;bzwWdvQit^9!4ePjrOXqGj)%yovi5dV>%qCe~SrJwc5!S z(hEW%5k&`D0q>x*+Ei-VupSo|U7Q{Tuu9tIDS&;*)e&n`0&=wFKqeq2aI(EVM1Fwt`|o`@+mLyU_h!0Huo0Ah928u zhaQbqlX?TC?V`d&V$*9hdsighmgoNbAALzRcKsJgY4^b>l}|QumzB4l5D{YkGtFUA zwD(H7L0MH&Z8G7o#@XgIE=}+a1)w8)<178JHF}|-S>0l5ZTf3ji-*+NDpfLQhaJ<; zJ00leY3*hXI+;8#MgpxNIX-<+;gcX*#pii^d||RmU@9wn7KdMqhgV>aF!qb?Yvf1P z%gGj+!@1^1|7c$+7N~CnEQO|OH(q{pZ5Cx4OziP+Gv312sJT0)A;MX) zha_IHRA5UgsnwjbwT=&D0lR`Wb{UC?h*s=*5ZYCLqMSVCOPdruZXwd+jE!7!trP{x zCWSA~nb98^h+nrwbDy{FZb27siC1Umn<2|DJpKLt?;AT&HAy=YdviDSi#PQ;5MDnc$MDlcHo+x;xPS- zl~rf9VlQ)RDebbkg@yZ7rXKLYnPX+8$%vW4yWxjlKd*-+QKIEAH%~AyfBE6asaNaI z+fq*T>n2duuiZpUzXsrqir>&M_P)PmBof}GMgEEf_oDON=bk9_#7&A)AOp)JE2o4b z{tS{b=V4=YnPE#H_O!|h0tH|I%ZuHpFR9INQ9Y0#tMX&NZevO_W7c z9Qwi1^398_Zn3)T4go>IMXm7&D{UnV zR7yf>3|6Zb^ugPKtTjZ~)20;?z<@fkk8VmR>#W=H5}x@6!7Ul&9|@IrG6e&#C(i~KuTY|lF|mxXeNNb9VKkeQ|Q{B7iYhL za1tKzClZJv{+OTFVZbWRZfe<=6FxehYu81^XjPlF8&jg1uDtL}ZLI5d!2u;f?yfT5 z->r0ERrPr9?C#Fb&%+qRz1!qgzDb5ZwlM^*yh&7 zgNMt^!W1(3dvoUzXn+mX(=IDcjl+}0aY5pZLr4gnyS_~yreaqp8wAa!G?%TmuOz=x z0(%N;KTOU|%;X|B@5blI(3^$h$Xk@eK^hxs%v{Gya^R#0_d@AdEolcNatXCm7C%Rn zJt!K*OeOW%)3cdi@cqQcf5sr(FTdu{Z?D1OvO&V+`TP7aG&D^L{W+{(PY)2Cc6JC{ zV%;MOk=s>ylYnnIP|%2duDx9m7CX5ikT?(P~i__DaWyUXJ4EH1&_U4jLN0Kwf|g9k$3kmvo*{GXkk zp6aT*tFG$(gK;1gUEZ~})#=i(2Qrv*Ap?GQcNg zvUu#4jTjiDQiVQcFSz}_vS+I|?0CvCzv|ABHsby`+q?rJwyOYXo8gBXe=1})B!kRd zCz#89Kzs+b8(~lZZnRm0fc-=e9h{){ICFsKBWi!Ur({`G(7lA}zFvwx3I zccZpS8t~YUn?G(I*mGC>>!hs6AJ}oy)m_^iim@N+?CjLh)rHtm5vxt3qAKNKaD+rH z+{Ypa%>!)=;N8+Sc{1?;T8MqvM7BfGctK)d0o4EMk4#T!>8`DHY<0#VaJ>{G;Pa}a z&!qk0X4R8JJ!(Lp>buV`{Au;B8s9H2BJT8Rofez3#hQ%xLb@)G3ywf5lW5Mec(i2d zz@y=Z6BMG2aq7?L;REOl<6kVC!D002TQgdp^8H-Y)p5q&tMQEz6a%Vq0U^lD55V{o zw-Zoioe0-y4#)vyTN3q~Li%@+t<09I+fQ@w=34>fI@ z(BOCbA;{H4ygtl!)C7SnYkz%v+~kbOoL^XY$2|D6%(4LboP*ZQJMSm*{qI}=a)CUo zhwZPPiZd^h7s_7)g0tU2En?wEKG)k7@0&0Qs&4|J;vaG-5~RM|rgH>%_%v3-*GbN4m1&B1%4D&9QHx|{ zcJ(Q(Vr`>D?G<|B_7mX&-j@kq8~8%0rC^(>i4M0@yYJ&JnxIO|gsUwWfMJHpcff?D zQrC^>o7>`wWx9k0-UXNVrb<4`6x&)DCe%38Og}#-Np%ES!17xEIIBYPzgqO5)vOi@ z1FdJk#nFx!+g`iiR4?E=8;qt#)X#ZTs@rHb{eN$nH;Ao$#p8aT@Yna*{Ucz&YBFG` zY%2HmVa+jtOBFbHJ8_sp*#D#YPnYcy%d8eGdtL!awW{)B6fuX(8v8_u?@y`rGh!FI zHvd6Nz$VBfaO?We+Vzk#e6fSL`aKm{816GO&%5bmYL{x&@M=-T)rcv^r59E0S;EEE?M$D88Yn;+Z@&d{%U4uE z@{jLfhrf)J;LFnbFVo}IGU&yFw69BHUh_%!3m5lyx@2uKo73+wU4Pt#<=I)c<`r{X zhgRN%UB{E=ao@F03;~K6JTkutwcd$?;ZueQ?roaM6sM8&f)>gOs0h~dWe}5FDxRF& z5=yGG6Z;aoL_7K%?yHy#3;$}Uv;jpSKaI+w3%Co z#WA^}o$PF?WvMCI)0*pCIv0zs!uX3$Z604Yifcdfg*OM=!5kpUoYGEq6d$laCf>)y zK1G3G`+kqVzwi52an$*j(JRJ9Kx#%|MU6c!3CYLP@rzu}(1a!^{uHPr3qtCDHbJmI zy2}5U`|2O%EUk_wTxGg1`dxUWAv6un%rK79UHXeEV(|PssPNv(UuAdVeLLvOY77Rb z7^==~`n+=i{%~gt%z1Y#kIpik#;8S_*k$U@5b?OmO=)@S}Oz#ETBiT zuR6gLiy)1*w)G4|BFa^n4{6ljg`rkJRJ!=&rm^$D9Vyz$*UhB>6h-)(|0o>p{@<9o zYsvCwetQR>?(w)UYVaE%POb9EJETAp=Jye5(3O14P{T{)l!GbWNBLh@M+pQ|w^wwU zvehBktCoEb_36MZgvVvg+9o`yx>i;P4`@~(hKmZz&G2t;Eo;a97Qii8T~}`X_Wa{% z7&Di1ot}>`+2JpnH@0Fn{52mN{95bnsf`*)+RlKdp_+$1Ad%!hs_2VY@epQUn}E6P z-;p0D71#VzI}Vdfs~$9P2r`k)S^PLXH|D>682#qspfYn^9n6mY6xT+7_8+Qd1Z~DE zYGMVcqvQV@LL^toedRSjxckX-%N%@!2WaDPV+UI1I<3u3hPx4M_{t^?sd%X4BiaFo>a$3oSuojC! zNg#sXg7n|yy`(NO4j%z-4h4I2F?Dfl)~4Ix06MF+=YFPpUM+QOB$deU6Cw-k!R)S| z9i;$bGw#>#1b);|VcITBn;igvNnZF<_z8()N<4XcN5<(@=;7ryDd?};66=LCZ#X9M zyZ**}U%Z9OCJHt^dnE5dDZqZcvZ^vlJ)Ojz!xS~{vBW6L$ON0`qkrdLdkI8q1-2Fq z#Oq1dz!E z9c-fiZ?NfHwcE+D)@2zL8IwcS&?rB(9gQVSPbd|H$3(+k?>OJ^hy?&(#6km9MLm^; zIR)eXS&nx#YnbnnvFuIn#sAH^<_A1SW8`<19pw_u>sthUkp!IobXPO+7A||Y zF*b=%#J(U}BAS1`QykaF7cHVvvcHi=CO3KXZh9!U5e|W!O4Euy$RI356W@e#a1;)n zK=)&2P@1Ceq=wBHMi!>$-Q_x~8wOTdkK$btIgHV0<-h`xwK-Rk5Qhm2o#Y1A%p)Zt zO@bi;bkI=D2gi+0ZqfvlMlqy+4@B*47I-dKCommt2jZKLG9e^|pl6K$ElNh<)i?1< z-;gSDSi`|2_B48M_%hvAvxhoV57bzL%aGDV+sRePMuuIBn?*ls_VHpJt06_mrjKpX?XwvZ4 zAPLM0tftzHK99Gi4`K5g^^+XV?uU&lfl79nIvK!SEVg)fBzC%#2}p%p;+Q-PQ8j$! zQy=IGhUHTl81oMr`Vvdgnl@GmoeIdjR>Y2hy4{&Qu_*Up|kH` zCuU38t_lRd3808vUYnY8^YFB`wN1E4l#ewk%E$cYJFKrv7a((K0jCIFUj*rGzRu}Wi>Jx<$=g#wo2rzewe`8EgEaRstLf<`hQ?55cZPK8HYxW>S?M+b)f4ZtJS1R-!{zc& z%B><=#;dj^$5LtzjRt8Y+AIOa0{%~O^0eWfYUde>L5|-fbE#I%L_W9 z{;TU^x9JW3YI80!c6&G8bb>QP-ERhnW3C%h_aYHyhL~ zo%YnFhiAZ2QJ4gWL937(5sF^Ag6L#y;~Q0$7DmXrt(~0mx6tVZ`|^18;0VW6RP)De z9%0gj6|2R_RSXUDzcW)?TfMhaU)q-Cbe(-F>_25jkxFvOYI>6OUn$5=>q5 zpOZA}`mm(&Mf6a;jLLKvCEGs?T3Co7Vcy-BrMoIzp>Et-BEk)X>OXv6zg&spAH%lw z0OY|tVC2itcQH2`?g(W2w5m*3GdM+l{FRCW;fPI}-IBk=3N9`1U<3f2vmb;0BF?{@ zZ+mojGk7D98L@KCK62+F`tA~*CmmnTGA(iS%WM(;g-OJtE@ka@T3jYki!p087ku~x z74r5&)gVcw&JCDbtgEJ|2s`8cyUa?0->y+Y*!4NoDXw-b`F9U?94!8*rP*xb6nuzz z6`+85;Ac}4lV>(qsPc;Exi~b|5mmky(%THK07}y`|7!$PE`1Q$ z+ge;m2$HYejxpy=Q(Y*gujeD$aQN}fzrE)>qZ|%W=l;7)Q)Wt;x#8I9bGWWS$Z5(y zxs+(ou}2KgzEuC!ol5oCTL%8;NFikyE>>fCX16`F*c+M90_4jac&;2g*o8=V%q|uq zbFiIHu}`9aV-1Yl7e%Z_l@i@op@2>eN-PH8WNp8Hv?nYu8$*WHusMImh;>orDQQ?b zaJsFc_OI_2gN1ZGy~imdLu?=o{#{T zowXvbk7P1)vrl1j0nwpEYi%L(1pmx>_-nI($Jogzf#fAO+l*~HnOUit`E@?|AK5;= z@4+WTF$b09FSwIbA{o-6PfZn#o$5U92cP(k3pQu;^-D@twFfiVHhBjL4x14ODO8|( zUfEiupqUW9k+uKwWSS6j3P%(=iH-_2z%gM%25mO3EGIW&c0D8y#S=}mssIb@4wppY zY2A`La`i?hdcD3?X2epLTZc6y$$K_&CY8_n0~HaJ5DOTjKYg*+DcZuB=+E zBt)_vYCroe@+(CiLJ_WKmbSDGa=!k8VM7uSU-8@PF}-fPZ_oXj_$F8BN70BzDw=(W}I8?auYMyqd59t%`MfkV_^%JcGyDqqY^M|CaJRS81JmA9=&3 zJWLnHvfcNU!R|73XoO=yt#i2XxYZ8D9uk5%Ox9Uj+MHEiJLNWtjpF{r|E58=DCBRe z2abfF&@WOX*5LOo4&81D+@i)0K8LXnaoPkNSNF$Hmf@$tlXjJp=U#Ww&-}9GiZTpT zZpUV}B_HUX_tNvXKRi2CcW*IYv`&`!hl%{HX(r?)U&277Ylp3Xb;AW4@oAwpPHG0V zHs9yOM}7}g=ZH-4ew17_lZi7gufjW*ltTo>0|rPotQNzd0W;Oa%n}Gt%#??HiLxn% z8S}asTJcstd973xGH}HZ!_8?q@O5BSo+WV8FaYFy=^N1R_cGM;rNoP#%yNYxNFdI* z-;a$roLvM&{YS--N0ZmYYQOI)?ml$zql7fE!fnc(9Sd|~&K(Z9d9f>fMq}XtdgxBL z&Qln|O;%uH^Qk3J9+{&=X z85(aKWtEBvGP7Zl{I1P~Pb)8oTsJSlg<8qgU=Gr+5Tiy$_>%VY2|3=S6NfewFjX?m z+Iw8kyU`rDh2F{Yugjwlah8D_MG{@^Y@6$NTA#C{yL&}f!kqq!YVG4k#l~P}#yfqE zH3>I7RenTzW$ptbL;erH1kWH%osAu?171M%LNT#nmEY_q=Xv-(RSads%@Lv^6D(|$i)Fzyw20N^D~Cwy@3|hn_g!DYK#gCkZy|xi;zYcUXzp5RPs}1rEqb`ihxmBRB*C z(cnasK!l}3*)!7Mji#R6pX1gHUuWk^lfKI!V!Tg@<$uKW#3zUgUsaEf8;bp~1nAxFVIsZHDHABS%OMFXWg1^Dg^iPC+Befkw>Q0Chdrj8 z;n&Z)%%G@rllc;Lainzc(}0xuU=F2>S*nRAT&Wj(w6p5y$MPR|WVD>wWt0gg8MkD< zsj;h*0osH+j|j(&B?DQ9uJ3=G)>}g(q$HcB%yOOM369LT%@u{CJK8lfC857HwVc;NTw4X9 z!NXqYBEZ@-;H&$ytmC7xVUXjW@l9sGFRPaowX>hGjb`yIFmp@=nzNSgU;Jy$crX1RA2_^Fr9{z*yE?#8zy*I5TP2e8q4N#FMAXgyaNO0 zJr*lFH*zrxrpl1q-*ex(o;6qgn@2*9evDWsNPS@j~kf}SAK@mS}6Za*c`np*&kUc;j1iG zz2mE-57KU7IfJ)9Y zQQF753}_i_2ZXZ#lpk?1`Fd_h>ipCE9deBB>5Pmx-Y9bYY^jAZz+w4jMvomCOKCT} zxLSd?y8&;*p4X0@&wN~z)PIq1eY^xagp_l;VQ`hBtEWfXOdpkYu zP4P^DMbOnY8s8;AbI%st_h9uz_B`(Oh0=LrV{sJ)ExIy|js|P4mfqvQRaEi%MX0zx z($;wIyVBu8hPa}vg@$k6iv3F1zf|mg1=gvZ=t93AH;mZ_PRgo~e=ubdb?uYZD(=4T zm#SfUf+}H}@-o+yP{dMlP5mEmZi7|)o(%KW>iJ>GODfBM zrF_=6aG$2J&2;~c4$H6>+*5Mp`O$y9?DfxPU3CRP6%n2hu!tzjX<=jGb`eJDaJ>yl zw&OX48Awqkay-)~&j<`_5ha%HI0X-zNv5*27uDAz%E*gplxc-`@G{`o>y);+5xOJi z5T`Pu%f~|Kwa5WU*3(&Itd`W#sdw-d__S{^U6Z0S#F`mlJLn>8R7RJNnrAJFP^caa&e=AzDZe0mH|3(S) zJF;xE3ut{-tNEXeFzJ1}=u)9E+D1`ppD2ao{6EudQCdr&cQ- z)_@C28`M(&vkYh1JuVGuOues2n0g&=OLsiSs4;MdbWS!`#wRIQt1piQn@>xD`#MCO zcjtp%&83X*7j)tpVi`&chyw4e7UqO&JZF_01%KtLB1>W?Cr({88YueQj?*I{)h$cl ztEI`+%0nk6qlTecQNz*aqU3=wEM`)Ed_-_607p(H&Gsd0z%gg_lb!+kCN(0t2BA{#X9d^OCctV_u+U0p zdmnNd6e#p*)bItZddCvpoEa(Mjqj?PlB1BGiALbPqfp0W6rS9Q4l3=!NJfME2)0}= z#ch{Fc)8kJ07iE zDOURlb~-J-@iXLB)|B28ohX`%3z`GQC4b0mYnAJLakXkG^@;^?vMmb*zR+DV57Xr8 zR$>pDjXHW0Dc^ORiajgA7Cls7>X_GNL>Z*G%4+`XWzKRMS>n3os(N*qETe^=xvO8Z zyD3t)1I6VVMl1tGT9>gVozP*ZGV9YcYtQe2B1k;&pUb?CmaK}}nCtAK?YRJ}^F7We zhBdnZLa{yERXEobT5YD*YHDCJP1`uczw`<+!zwVCybEaB8r6bXtKFtyJvovg9`ouw zl}aM5#WjZqJjGKW_B>IxDT@(A@w-c3+NlSq6fSt*W*U0yUL>S+Iu`&gRkAn=D6gaH zly_sg6AI{;vGBVPtm1J%!V^rbsZF=TRFA(bl}CvYNW=mNKoc?j!j+kkVFh&>t!+Mb z#e0ufUcUYmeRT9GeeU^hU3d)@;F&UE*RH3)#$24LtbWT&KQvm%5x}iw3a_-|mmz4n z$)OzD$;fj$>U7TZS~f=b-gpC)=?ZqaDEaAfMK{MOzVGzOce|Uh5vqm(U+ttUKDLJ+ z;38u=V}UL&2WpInDfUu>4y8}oUJpF4T9SEts90KgB3s$B<*nXqrWwtMD*TN}-`-El zu`;Q(NFO9XYP?@ETT25y?-;`_t8NodJm@uEz`a`qaoeoqGe z-LG7z3NU2X-}ngX_%1EOsF^u|4t7<`YI2P) z4mHkX*lDG$SG{t=-PM%&FhXZY^Hxk+O{b4bcv|ZZlewQojt|#=x^Ef6Jj=tGOhTtH zrskO<)f{5im&Ji367W3OFbCxF`zdg7ZR?QR>y)?zIj>4hp__`GF=L2|yQyn|Ko^-8 z{x+9uod+^zH$qtc1S~mU{a*hj+F^5FDZ}u;km|*c0&@AH5(C2w!kJf!?=;;oDPb_S z@I+vwqd++bts=qjRif225LW#qfFU!^h8F*DFt$`ye<$oVqf>_CE0~6;c%|1P@#_<< zHezOQ=qf6b>mg-UaE=QY_M`XaSV2G%72GDR9zDYc_obb^GS7q$`cX;4Sz$-lA2hoy z_ZHenTvSuM9h5jWKw~Y)@GEAY>_yo@+A-mpzk-)1AfuoY;pc#*>rVlfWpB>rL+=^@ zP({Sb+kBc7%Gjj;wFVI}7w^@jXjuVSga2DjOeo_7Y#*gE-YN!wIXOe@YRrG@+ZLIF zv9YP(s8Z4RN9HCD-t9_Bj=`w|MiiB2;oU@Ks9Wp{WT?d1Np$8)2}bC`7O4tHsRI#m z9&H7j=14Ak6pc1tmlVV%Oei{%^rjF3>uP;3gLXH5EMqp@G9^rkLk>+H0j_!aA1IEH zTx_%Erz#F|NjV8lZw08j$cD?8HWVI5pU!8c+JDi>OS=G%V>qcR+Ho<1|NpZ9V5178 zVD57{M-Hv2zP!0C%mxj;uBy`rZ|_Tf9gss~2#ZO9(fR;V3DHhm)eO){_gwLu0k>A5 zU8|L>HEk4bvIrN*0W0Ra`XPp6QX{MuwP`i(Auz}FKyE3Z zK$C0^iL$WD=)?ufJ&3kE$!f?kAVRGq2}ji~Vu^rphhnVNeDRN20m{CBk;O8H7h{mJ zd|uw~cp-!AWzLpfj&4v3rYS;6)rmi6g23;q!$~jh1ddwj6WSD{)OwY~!4R%A zoP842(PP@NPpK~A;_<}N-bXHGbip*?Tyn6G5U~<-G53{LOF&{1PRiZSd0Cn*p@WK? z!JkYj5UBIk2WMtm4UF?fs8B|84OX9m00_+qIj_mQW)B==p4P4O?XILkL`>nhmz5;N z0n&~>*CS%TU%%IAH~1RxjmI>505kOirQ8)Pw+Su*nAr_?)>CbhEOkZ9J5ZKCH-278 zcI!FTF~j3mit=7SodqI=sU0raj#QTp)&V0TC2kG>S-y|Ya6hFlHela$G#+p;F^<6h zH*rGOLbXp0hMs;Cq?5lX|HNjwg|#AFMaBjgUS9u(ZqQ_LvfUR0e7542N7odFTUh=% zW25$Xms>h1^wOs;bjiNI;#sRftH4@{3r+670X^PvItx_W_tDhXXidFRSS7YXwbcH6 z#8Jt9iMZ;#x-A#&ur#~BAmmB-bAH?F!kd`2#9Anng=9~jWco2%QhXw%u6Rv04w0RK zgFUKBV*m=SX*bpb5I&wiPt*5QW@onnIA7ubSVZ|)I_LQL(NJAFu8h}ytGK>GVYTHB zQJq=n3_A)_03l*SgAt*7+xU&!^1BZPz)Qt+-otGEof&B&(_n4NlO&p`-zPt5i2;;C*` zq6#8#h&Xw+ax~kj=@t=e6Xyv-a~&xscYvvg!Dsb`Y*?WjJE1<=C~uKR!@?nEmb}P zfi!n@lBJOE>qQ@Pzv&0)@~r)s-g3Sy{oD)SDX=^WBkM*BvMwO)JY49o0j2)@3Mg?8 znRhO(OKmGV&sC1fjPkNKV$}cht=^!hN$J+hW!R}d?cH@5dIi`H;qN@Qe#`5^=<5E# z2WXk0BG<4x|Jt6*Mcl=aje)&}PtAW0hK<4^rPN(Wb?~^}rACZGLn-!pyt@<=$eS;QWG(J$&SdifE&dm~d(oS-RyP)0wX!B_tqF&vk2J)& zr$2J%mdupbwvLaT#Bq)v#NRW_>~6$R5>H|>d1II!LfGx%KVFiIn10&3`MrKGZWYmg zblJJBe3p+%4KNbmfHp$JU(-VB6^00QN-RrM+0cB1(*`BGD8zO>N&!ND;B)JGZ9EP{ za(CJ|SwQO|_TemJ#|>w>ve@*{;gk8>$2gN)ageH_RC-ZA%`taF2se>1lQbsfJ7nOC z==EQ6+NNrKNdQ8_LS!OOt>0>&e$0NMvLCBLPyS#q+~xKy?38+vl6-qz^dyz_<&SWB zt7q3L?u5o2MZ9E1avoF~?9scpVglY-lKJvew`X;iJGp!pdv(#Aq zNCu#wOrl1iFs5XM8gA6~GmO-B%~!0BUOHRnESVC$Ud+VOFFDtGeClw_ef+T{BuElU zb)k|}1h7+wGoR#GXmJyueKJ^*E7QBVdel>U*EhrW2sYf6?3)7%JI)-wwPo^Dq;%CV zIz!Yo4+uKPbok{aA8j?#av3A^c^u|VPF+xpEaG84?7Amx4GtK8SI{``cgZS|UoM#J zI&(uV@NdfkR|F&#piF-pX5FJm%@8Hn7|3tFuaB92?vDVW*UDbTUA3NxUj@8j5Ii!w zg_U4UH#APpx8&GI%2!iMKF{(h{E*9xs;t6a%NUgH3d`FtfmF*q%S~*SPP)--sB9(Q zeH=dfb9%lPajvRtnGS{ZOjL#UNBnE;C+$CUhmg-@3R%*^J9Yj9zHP|JF zT4^WfeE5DxC6jz74c2C|zOjhqjZCdE`&5l@ADId_JVMh;J&!z>EQI`hF&pz=YYg5FY7JduB$dHFsK9}k~1K#OXdn&t`V z+-83$CG=OW-3*STNV=Pl{2>E%kS(E;PwLSMW$JhJZR>eA<{db#u1#*JRHc7+!xW)a z-#JHs#y8OW>2R)zL$G7=`}hnk6syYUW&(%ACCE zraKavU+-CNnV!$Iu%TANZom9UWjKH3*Pk|aqB}N6^qdKNzi<=&2qFE2NeobwPji$O zOE#6F2~aB%rz!b_<~k+stftpeD;gv{0^KFA@l~Jyo*@x^xDpiP(;Q7&8m*RSu0|Z3 zMkt&m@@cTkfaj|Cr~Rs`nB?olErzkv_R5;$Uoav}mlN>w2(&C~4c$v8NX&$!45sGqf&zAeRAZs>{{ne>L4-q677?%#H@#!p6IzHup z0jH?%jMr;b43(4U0+n>dRC1NKr^Y#4nlKY(70+|ox?3_|{8$LjW~`6ta6cs-cJ0+% z1{@&IU&bYh{_T9cxLy82fId-(cxmLAI_1vf<{-)dqe)>=Ql}t>n@pbVC4n1{T7zK2 zdDd2?yIANFz(k0a%Z_G-(>xPtod~kA#Vuj5)y>dp(Lh2@NoBZLEx*n`9g%-jOxzfs zX%<_}UKDR=8y0c-bthoyUB_I3Qki;xEqNGl);8_n0Q7oO58SlkdP3RlVvz6Yd$*q8P8xhaU5UT1V(if$^Y`0MM8K`@&n0$lvd;?p0D& z(m9${Z%;?0ui3rYeFPJ?M&g&!8h?QnTiGPUX)CES%K*Da#M5hVfrYgQvpY-)2=d}8 zuA0R@od5}#g(x=^FFq`+Xd@`s`7%wrK|)Fh6to@Ji>@%L5HKllZETr0@@VSPc>t4s zIClxWxM2Sg z@_|7K_ay0?wJuGG4Yxmak^zI8WA09X%8vMyc-fj!D*HWezP^tlXH<{REte)2mCeF2 z@_ydBom*DCtfy(F@Vr)0+N%Mn!qmCucK(j3pLunVLw>>_R7W9w;}+UuR_C~gHex~% zmo30tdb?ct5!Yw8n!+@Qg>~vYPCSSrKBL$DyYPUKwA`)Qrnra!OVc!aW?`x!Gnake zXPWq%1w#BUxKZ~kNhQ6sAoUe}L~K}c*_XrXp&Fz1pQOptY3=0v>&j||F2_9&Tc6+_6ud?KV~X4D}yiWt6~-%#bE8$K2nlA0-bBq9eVR|u~X z(6<(ilg`5w3i+FUU3yD>dx4|wQzt$W*O z`ul@?yn1OoX(ia#U*q`hTdn#By`JN=rX|PUuJ`@_rRkT$2Ntjpawa>DEa;`V>5|4A zQi}SxB{3C`(#WA;DWbN78rt!=0J4%*u=T%14WIg1{U3aZ7g1>`76tg-znW^7(Cg+) zJFRp~Mo^jJfG;H_s)QBz_2(vZ+J}`|ks_fBr)zvl4MT5kn>r|K2~WP5V&xE`85LzCY2z$ z@z!xYrSk8Z?+KuReWE{Qy%+bGe8g{_4jX%xz2=uof%h7cqizt=N(s=|-prDW%v9#m z9H+hHzna7+Lz(xY1cH_OZ>UeVzU(0(Sr%8NR2iMWX3fy5>3{r{P2mSUa^t z6`@$`n*q8HVTYg)$0fzozAlIM3S(rZlVq#4H>aXBQE7~aU3_&u+MQ-KG{8fy`WIIT zL_1eI9EW!h@+RlJgW)iaFG>zXvLXSil&;W>Eot_*4PA%6h+u zH?gdKI_C%*sz8ohEtY5u*D?1rtHTIZWnn5ZrD>DXloG(lET}M*6Gp^=LPEkK2*agx zshvg}bpDmfn?(ikfBG&-yrifYtl4N*_uF9ChlcL#;$CX&%;zzf@lx;Q$^8GyJF)3b z?oxIZp7p5|<_8_s>%Ed75m=)4=3qFunc|8S3;|o(A#eYP6ELjai7F zq`HLUcg7Mr=8UCWl`pajDvIt8MdE4vD`>Lq{{1H<0qSXM^$FJ>TI2j65^0!0BmQU& zciSplGvk9yB^Z>5@S^_AcJ?sw08|)M7&MqzqbBW;(k+{|O9NiSLLk+3{6=Y+yCloK zfYNs?vGFf6{9|TJVI#>A>U~<*I@Z#wI1$Ckg|PGBJt9IQP;B4|fHf8eWsrSlGJfAF`{Hy`#HO88gMFvY8l z=l{LoYT2E|$x=pbhO=D!GTfkIP0*vW-U)#5bhKPI{JNpT=G=1kHqZoKNSddU?8dw9)m(Ugcv7jrYj}c#03V9q z6vi=woFTez&F0(6y6|b}sOQ|?YIm@WT!iBBp(^UseUXU(w&#oGG>Bisxob_tv4wNL zTEF1g*UWAhxO%*lhn$AX>F>&-+}8R2NeOHGj3tcX(A`lfkNi+cgvM^OBWgaL<_F&V zj5L4P1~?V2&bDqOAge0x%c^|1Jgh28hQz6H#D9UPBxgdydc4{5sH?hfRY3V+N?il_ zrI)NN7#zN|pExvvCle2-_61aC=;Q z`BpyWqDF(oj%+4-P$~5!XB(S*UNWkEl1J42VCc*IOUh~eV~pj?Mwd_^iIOgdzG`@~ z+(N?qFLmI|Y*j>bqqNv)$J?cPUHe|`v}o_kYo*HXaS>I`BBCO(s^>u}biPdXVM5P( zTwHflCa#*0CM*gtc~B(1PFz?OS40ro@dZ4#fV=;Ph% zhk3t#*Q~pjjQ!A#`1ta-jh%p{#wcjv=v9qgt9#!&fN<8{lQ&;Yq!r; z)u!LgNn6AGsRQ$y@vxfq1hK{2Exq3rtK+sYW4R}m(e@HPsfUiJ_k1v#z0#Mjv|@Kl zv{r58R$|MA^=P;NByv?5DRQ!3`eEdEk3sFeBk0_D!BQ zSNNueF4B5>hEagW^`6at&xbBRLgMW>iNobkbsn0ue47+V zkEFGoC#ywTUp?4l3Z;o1?0$G7T1Nuv+XXari~veGuMUL`zn`iHKF1S(eqrfa8n>ev z1$en6eR{h&-wmzzKAm>h6=w|ij0w}L)CYaZ>bGJ2H&~sR2R3I_j2SE@HUs{OWQ-yO zprn>}Ifl*=H5eFCns3AgE!Zk_wvbxj1bx@ za(UBbeOmNBe z&doV87s_SK^h1K7G!`u1{7A&F($Iu#Un(T%e<76^gt|DSJtLmCNIU|q4`u=cTk6Zd zNUyIJXJ4(U-ENbiAKzmFz?@8+MfE>PI-V8C40>M@w6eqRPqeObozEeCd>o`P%7`{j z=^B5U@X>0_rL5zPe^kBw4Q6!gIViGSdaDqK`d53i;R~yx!r;rK830<(;l&!7vlYRu z_bjST?;YvC_S6~Ph5WE>t$ZriAt_JtC^jtx?z_rsj&H`&A$X(IxF$v!B8qhrhZuj@ zjWxA`;m>a0j=#3hG}IfcdRVrN1xmoPW&0ws#d*I8_!^RB=BL_9yLH;57kVs}Te3?f zB?QT_5j5zCsQ6>c+_ft3=q+o*rRheoV#bsDGtD}mX<3rN~LChR1j zgrNpN;|iDt#iZG(qd@Sd_@_lq(o3Quh&;x8;Z`~pkSp+JfApRq6ca@R@_n6Fm}deJ zSQqiT+<%x}Zuel$=CT}i2Ep6{5DArol_6Z-fb3p*8irWH1@nby!il|Z%iU&o;>x?9 z|7yV;L^%tGKUT>AX)J+UOv3pr%I&*(c|?^83exhx2#C1w8vVU~3~hcyckD+c@R4&`($~GF@J~XDkZ%ZRT@2^MgCw(+#<>Hq_tsP(>*RNh>pHTpnU$Yfe} z((AV&8v%o3oQqd|tgakufl*81ft~WF!kGS zPDJ5rZvL5NjCUkk-1V7dNiyW(BAY~_%W{}CzKb@p`m(e^p9RH5uJI93@A1rrQ8{5FmRU{m6%F8~^P75Q#+Y1(p-JT#*L77XT^6lyZ7u%OjO zpz^Ge@DQS`l5!A|DNS#$*JFxkV5}i zOfr)9g%L8Lw{RlSwehu$L{oYAFUqwQGf-l-hEBxA1@|nZ7AfEmB^{lN*&2%j>=H;fVV8=~<7CXidY^0Dsq$!_fM6k*wLT&PFGr6Fp@V^C>na;Wl#&V{K)lpGwV|W` zyf_6O^D%^lR}GqFC2W>VyriOF!+;?-Gq@;D@aL4m2p$cA9ZY2r_)3YDF@La4ks433 zFcxxn7UT25+FnM!vKvo)%%A*%4L{LK0|&NAm9xUHjCmRL3OWCP={WYl*-=gTUDBJ! zRZ6u>y5dPL@@kLJ!)~u5%z=wMoY{F1r3Fz#VtQXU5%TLns8~+vJ%sNn727hFVhNGX zu)@W68LfnttoP;_FPQxK04E0FGf!-KFn8eV|D)*}M{B zv2EK)W7}vN+ezbR|Mx!k&A$Jg&&=%1cg~sfPNk6guh;mK8kD>nF<%b%8)TPahfAM# zptK#t>CbV5lE$0wR4(Bhmu&CSB4@-oa%5;G0}rdFPXFq`li^Ol_`_;Gq2*eD=?os_ z2tR9ZdtYvY%h#Mjw4o5Dk`8a$p-IDrG%v3WOaPlQAtwK3`M!~H-&2hlWitNScoO~{ z=zQe#tB-oAGE9#%)PVc}h+Z)I8za!$+~GXP;2(!Vu#a71RsMK435mb2zB4R9JP05P zC}|P@`9%f1DK`*KZLTimz+_?&D#(cgmBAFtxZr$rO(}MKzT;!|bX|0dOB9DC(M_@Y zCjpT+drjJE&<3Hq*?cGVqxbbR?tal6C`Q={A!Y=E6y{-dH zw(MHSs_PY#-Y@oh2<0LLuizWbEsKFSH_<9fs>}(FaV^7vZkvMZRrq>>Ixo$LR7Nf z);=N$-@#J3is*5Bb+?PSq>x(_~d3!^6cVsXvIA7nnAuea~6fDS@cs1 z1hICd6PbnS2JwbnWa^G8=HD=4G#L%ni4W1m)}ifPO-TgN{Rfv5N(PG}p=y;pWeJ`v z=oE@bfm(6LO-O6G&$?APHkec+JLeD&_yM)QnsnQIv`DLwT+>b?7aqS!&H0U6FH<^< zgF-M0X`KY}!AUh`cMXk=)Oxd7)7Hn_eC9su7hH{r;A#SFjRKxzFPw zsKgoL1h^}LvKK10a{V3|UI&9l3HQ7G250?i@Mz%cpXBG>LnIpC+&V7n7Dfez3}2?? z+QZnlEs?)WWyFDNZ+SkS(Vk5%2U*Gw?KB`B&Tv;K?W`$81zgO0fi5(g2Z3dtE`|aXxiA$)Kj+BNOpezkO5obzjI$Tg$Ql94`t#n`hxr_Q$AvOt_3?no6BX6vV=| zXLoxwyV-nS+v=Wtyqsq(?_|Xf&fH;H%+O``qukOE}#NKM(U* zQS$6it0$%nYOK@X>wSHF3x5u+B*tYyYkk?CO2Axuk%PO=7^Dxo`b-*(<2|;M(|=!Q`SoMUVd|v7Tiuk)D9)-4|7Yt!ygLUS3kKpO#R-MD8*Y z9-z>>A|Y7%AIcG9Sdt72IG6B^k44kk*KX{j4C9&!tZ^g}qo426b$rFShg^$TCrd!X zDdO*>Lww4Dy!+EM>~QreLt4+6_3Q~rc#|8FiAz;cNr#A8jP+9J3aT``z!3Z+C#yd= zscX1fyugwISqEXDvG7sIQDUrc`UTe;;p^a88N>e!y(~;7!{1*@gO{I0o)%&9+MoY? zC_S{31Vji>ReIxLaZT>;oIeTub255cvCj4DUd9*0%bK9oJmSt&E2-3lRaE7)v35scv%FoG*uZ+d?RLyQY$4ky<4wvJ({fGcSyBr!sSavpOT}nNEHGlBc>>rUb;C zNi;pjg)588wNAltIGu)z0J2jwS7`URP?Z)?o|X#cjG}kvmr!|9)uynh;1SWzz%v~S zdNnwyM2O19X^n;nX#EK3L-d9*GM7V)q-gbWqmpzaN)2$^fnfDU%F-!OutNgT7u(|K zX+Ti+?dp}=NhPSq1aTP^ZYrIHUH(cg6JRRn2KpZrNIZ$40r<=(^m`)<6=+ra>qKzaoxW(!zEliI~pO7H)UEVjI#CQ&bsY1cSVI5qi?^JkCm+@ww-4 z+j)P_{;;llASzA`1L;xzHq=JgsvE}G9r)Z!4l2T1p zFjHZuO&HyB24#ns(0tc&=};M_OU|hF)f84*x3GS0a( zsx4ygxQ@D8RuZ_h@GwGv&og@Y17*xt3)uAX;|ODv`F@)WfM3(Z`ja?JbtMJNedZZ ze)$uL+_8Xs1ENr<{bKJL9Pms%wRSBOMTnOso?qOdaVu12Xh3_OOXHOAZy3&mfgOM= z{?zh6e~jh~sosT102kWsL)l5;!XySLfI5##&cUr3H=l~HubfY1G-IX~XPOFWqJ5~g z$gvuGs;rtcR9Gy;FpmC|zUx>d-88Ax4Jq%Pc8tzkDqakXV#T6@e^kg@Vw+HARTQ}F zPb*Qi)EdfmjEGwfIah=vld5nu7tFV zG8_4=P;C;VMxEd7ad)CjAFZV^Ed%k@Vzq=?rh)8*A4dd+V*RAVq^a>5rU04n7FtWRuVI#J94<&^Hcz z=7|A-d18a7o9V4AR=2)qz#EXKih|hAUPD;ded`YUv(w^Swz_wEa!7IE*>S=DT@do zlk7cUB2nwzl5!pj6)_Q0P}7G^`1sp$jU*9YEDjutk~gj9tPrzeOh}8n;#=q*|7V2J zDNoL{Rd3m?w!ryqvTxyyQKXs-L_jRcSA#>piaD~7kR%tU7#%R*c~kEBn<^mk{0+Eq z;|f%$EHi=8sP|mAOk-aMLwtG9fH87?V#L#s%0Y_Fc-EdK?$4%5#?i%sBi6A2<#5A0KJ*q?n_Kpi5*TJ z?kkLwNduf&?UROwD#c{-hXZwpTt=bKHli2`g(8QP7z(?GMVQswcux&OtcFvK&A0$m z511K)jYg~{Xb-T4wp`s;?R}GM%>Q3WNXzcA84Pn2{X^V+lujNz!6pqY&1j7jlUZ^n z-_aY^8l>n3VK^YyhT4kEho%iCj?&0cc6~(#F-BR{93WfJDh7~U`ggxTWu5c!gdU3p zx!1g26rq^*8=6|0VyEQfO6F^$Yb{Mju7#s%FM}%e*`p4VGE86Xn?3$+%ODULr`#C!~`qr*>FV&t#{bI*zZ7<~G3kFq=T0mrd>V`PB{ zNOWn|L`4IH2S1%R)WoYvWqtK*gpIeCv!O}IL+n#h=CU)o=FxQQLDh#92HQkIIaZgK#$%qSK1b0Q?sm+f zHIkro2@gP&ufwEM$2Dr zRTrsM3WBM&zlO^sqtqZFk-A#uKEl9 z&CLvl1LR7B;=<>E8vB$8#{^d)KC*cmWwQzpWAI&q$0cyE`eTP+mdD*jJBl-|L3pGt zr>AXpPT}H4JnYxU)mQivTyaNx_NW@vs}`kMM=e!Ar3sjoly5@$W0J_}X;Cfq77Rc< zUQ!NE|LmarM2CBUyanH;HyIvpvWCibla!9wEh8C~aRMCzRaFo4H0k_UxeEbap$-lg zM0VAAevYR5^A_Gd@jtIAWgkF-xKOo6Kuc` za*d?3g{WJ**LHS*f9#w*9yu;v4mMPd1*Z)``h%?!R!Rw@iZ07QwQM8!9gUFNfo6zb z$jkV-%xgStNAg8AQ(Io}Qcd{L1K%T*G>m+Dtfi-_`(0C)6T@QyA zHFZk~Q2k?%ZJ`T?`ebCSw$7J`9HJ0$kP}^_-_Gw7wpF-BE1IFq-6q3c=l-lL&v&>D zJHXxp#0LSb-~y*@9$N~S;&d9t4Z&T8b=P_ObEJZxhQ$KZ@Xnk|bq5mOvNxz=Cn3$N z(a`}a`xPa_vEi1F#UWMo>WmE4BB|015WygA*p_9$VO1IHwtaDa?mA-ldsR6O?h zphdnXwjbNsFCedjWEeO!J>>$ZI|8tR;ZsMSj(M}+;&MZ-Y^l_i!5R|~qLqr&qIZ6| zLTB;=f&k2^CO0hHgyLkazc=I+YEg3T=~-p)?s)902YO2$iVD%TqJTu)BUL{vIloTw zO!$L5E_mh0fwSdb5CDm%O6{IhHkL#2<>OkyQ2gim=?sNQ-0^s-U7}wWr*=8rouHn| zue5f(Uy91?zXC&K>O51XoM)uNoILm4qCAkYNk6p`q7q}tO#-hUO-?UAay>`?mOj}2 zbgFGy|JIH;mXy|QNFL8|DVY|SAuqG!{rz-yR?oXRu3?aLi4F!h1oK?fD0H{*H~BIm zOpIX#z(s)Nul8+)nc@#?0YpfN%xojO6?n(%D7tJ zo~ys~WRg|uoe$l0TYjS~Z>z|yGT<30TMm#MuI&2jHgJXV_k^skSbDx93?|l>AJr|3 zXk)K5v)SzJnbECeL0Dr_+{^=ljdHcfJdQ@Ue<0kJnE6dne;ofML3-p#@x)t6XQ*k2 z*!h5NHsVoP5|_EOx3Nt!Zxv3t&OuuS@rf6hWXw!_6h$A-3qDOZsE^6e73YaJZ2t_C z5Z+yZCZ#UuP|1fuw?3-*iPP8E8T%%-5>v4JqkD;c$x4IDNAPQG!^J#-SB0!2A|`L7 z1nYpD`7wRx+VR(CDux!WFj3noyb9~Qj$GE%lm`*nHZt}N@KOiorI@>xVh?Ca(gxRQ z5>ZG(Jh%eR@kCS+VZD-%_y)Z3N7aj*_ezakb%xTB2l=Fp>xB2N!g`P9od%gc9LQ1V zaqx|mb0nWBE8%jkv%Ty-$Ik)=ZILG1Nd%1+y8qr_WA|}~F~!q<3#C@id~MwH!-5qP zdm2(S3ak`-d*atQt|)NuELiNeM9oSLW3||6YTDs;da3ha-CFP1Dm28>gxmLqh39^> zONNKb=h_mdDgs0B+jqC0nusyXe;=!wPlW}RnPwyENIm0n3l90yAOQs&tL%FG@l#u# zR?!QYTLL|8v}IeyL9F(R8--|^lKyfzW2IW51S)n0tk{HDV2bVv?8A*Jw(yq*8<7SaCu(7W2(ct&3(5q@wT~#wUrXBU#=k3 zQBTluc$lTT(6*_`Jsjbug~4Y3TGx%7G5lm8MpT#4@~TNt*Pu$F!Rmw$_w+%W91Y`t zLysRJGX5Kp)7#J*p9MBqkVm0(of!*|HnZ?777xo210JU6n*5%qL)W9DJ)#dpCxh>^ z9^Oksy_d?i3)kIl#jT`_9ar?K@23udX605H25t8TwHqD8Q)%S-fM&lZz9o*Y*!;#P z7f3)0XQ}mZeQuVwfy0>&u0d}?9vxF&NOoj`O?od{N#d$ZEmUV)stU9jz}oFTG-s*) za`UjN1{L2;+a+&p2Ky9=D$~MmMKeWJ*YcFC{nI?jMWd$-aXHpt4?ffWB21 z^sUG^KiR^yypEyAffgL*1%_$jv;smZ;)R7KUXSEh@@9PJ^WJ~>z8oXjyas1tl-`@u z6fq`aFB`xV!hUUgnXeHiy#dZx914gtBDIE@|D~dOL{y$4j9-%lE^Pc}#^3XRM4eLJ zI@hD`QT*2!R-H=9#JZDjVIR4c_-@@h_d=f9t-M~v)G@hVl@jacQJFuSbhy+^+sYn| zN-O!`&Y;O=KMrftU&i0l0O7GZp?sKUnb3(bLBgwHU>EC`Orzx2z~24Eg#wbB1xnd0 ztDg&5d~W)E@A0VXn&~2SX_v8C#zKND?yG5&l>9%Er1WtOyYCO*$3uRN# z=Dyxxec|#@{t1;Vr8Ofd_>2vO?(xUCBh5G%_GP3+jLvIm39TT56&JjA-HQk*8IhVV z;`q~q)OK-rB?KbUF3<-n^$OAUBlPavQo!n!C1kW4CCB&WT^EL}x_aS12D$^E@AqVs zjW=h>nZzE;JPNR&sH?!&G{32h$D=GqR&yP7y^=*X@?&Vc$$LbMvg|9aXkR{_HQc^@1Hd?K@7iQ{W z-q?C+Ck9bWH#6g&2|{%3_6Rg%Ja3OM-uMN7n&y+iqu5LMIVkG4CeXo4L3(2@QPJ;I zilOAS&Vn&DP_v ztbzr)!^Ay9NxlrN5EuMtdu#E7mou24j{R+fa$Rm zjnN6)5x+hhnsDYa%%a=PNmUpW!?u^V6d-~A90r`Cl6-Kc7x$3Q*j7p}JK zp346Pi>+`OWZR5qUa>5MUW?nPL#2g<5rGI6$&CauJu~5Hg*cFG$g<5#$iu1J)i<3K zFMFIIKR$_IRTn`>Z?k{e^JD%pCPob2J~JXU11gM6)Y?1XzMtA!GjHzKAF04nfu2S@ zyM7g2%SX^xoyJLAH4E4kzqR7Rss_x>)mo4-RiK#JQZ2Bh)vwtBvq--uw$u6zE?O{X zRxB8~Gl4NgmfyKdP#c_r%hF@C5;S4wO=f=Zb`L;9hW$QA1w^dQ_I$VlFdui0aSSm2 z89oW2W3G>I`1$3C>w99iqEF_Ct7;V6Mms-der=ks_KmA(U85tV_IttDqZKc5=J1u9 zw8gKJbXi3;6*-PW3Y!ZZ``-=OR{t+F4IEmy5$CIQyXLDI(awju3?9B|B9rKtExo~N zj$We`+gRDQcF{$bWHb>=-#yyOxB;SL-Ih(rc|Gres58o^GOHPnN<&Mw3!nckL=98X>;J@P4bB7kNl4KeGV3cQn%}N%Qwr?PW{mNQ&^7L zn2MOO5I?-E=9NW>$>ARLgXHRmJ3>ZjpcGEgV`(W?ADUzo8omCBd_3g=3iO1BUDbpPOmAKhHCe71J(#fJ%Mw*5hhx$(upR%!M^oAd3QB~lB;#tE_NN!g zEk77)9ZQ}cBU>&ejLdGu2(&&QI8Z%maNfR@sw25do;Ip>6B)XiHebwcn5M_TDl*$5 ziP+2$ra0RqB7`H460vMz4u@|7IMXS70Nd zA)U+!k4V??U$?ZL`^>qz;}{-UtK*Z4?cs`PDg7=tf^i}Ij^tY)pmfCQH~Z==w|SGnkA+pcX(~9_`lIDp9p{(07#813 zv+BM^3|}r!%Fc}vujis2e{k#UVF!u10g08T0B&=ERJPWlxZ3(}zSu_*0&A|4Y`asn z*BD59^6LAjs6jE#PmRWnCgmC!q6)5Ul|8ro0Msfe1E{E!ZbCgF4XoRxohf*zqKzW9 z7`(Jxu9k|X!6}<#w&i6V%r`dEP(k6QSQ*Qfx&vj?v z<~4h7%->LSa{rUIuMJ20W}*VBh3y}}iH{Slf6dPzqw+AoALS4VW``AO?u53cQ$rhv zdN`%=bv;~`?upm9npsgY0CfnS*N;NC&WR^HrHr-HPhhr|X;d_a@K!7d^;tstQhF5WGSXIjpRF2q$ zRl&*ho-G85uPd9j=%DtHJu`#(gPp;h2a9@``d47 z@|q488tM5|m!69LO{I=rlSHG95fpj2FeM<>UrSMgv~Qc97X4ZX_KUb??l`kSMV0QH zRUDIf6bHr1iM3dke=dZy-n9~BcH4cg!soj>9>dn@!(W_!oFkg2&fv zmC{f&l6WkQ>H@%tM;S8P*Yx+%)K#e{D($UwdM?`a51AEWFs$fFeY{XX2n?Pd)a?|%p0Gb8tI=#qF^8MU_98$m5W<<5 zP_{9$W6d_;QmC?YZCZ|M5O*qk^jmt@!>N|8W`57L^y@Ky>E0K=La<0_4qduaihJ-} zB6gLDE-KPtp<%<%Qk~y97%COCq@a9V=>%rAMuNp$&}e~^4Mtvf(b=Qm%tWNg^HLFX zt<7X?bcMrnD}fG$&}boGhEv8ZP9et`!*Zx_(%3CU5h$8-Gl3NZk*|R;TR%D;$sSSm zzi;BW=9{(qwU(qbT|LjJjv2+0;K&RD04rL$~sCD@#I8MjJsy--q5Wt_;=kEc)m2YBKRSMu^704EPJ*_jaf zmHF}>$dMI~b&gyu9o7E4njZ`)0hF$P+N(#&$%!kRjDMUqdoFg8lHc}yciG~73t zuzu7mI3*qGB>-war#?R}?$hNAq*b|hPV%bJCj)Ly683+sy!B8<%@oUgfo^GIMT=~`}DLV!S^W6OErN+xv zM`9l@dT8|;F%Wc!08y)2GkpAw^B}d*pi0N)=tumhDN|^C~g)f=A_5D^bN?CQA z0jU*1qjNpHb^yj1#xLJRIO6AOv6B<&{4LvnKe?_pEk>ryKROa8wcwYA@i`*5FAxpM zJdHFBV=phbff`|ydIqmT zv=TC@LbWV5t+;)7Njchx?#>wP=D;y{t@AQ`KgI&$*+miqVeAADw7YWLZ@PAW^heVf z7W^K!_?x(_r5RE?x9kc1;5ePMIO(a5m(RF&akVpL6*mROY}I7V2*o-DHTM3v#1xnJP29@N$xd5JsOVdk1{fp5?zJ_XtgYrKbw%XzgF|M(GRv8m|2L3e&=}v!S_-os>XI+(N?CN#wk2vkr*@nl#N-<-`D?Fu&O;s==&CF+ zfKF@7f?Qd9IY99MI$34|gwr1N+v0&DaMrKxM{_eNAen4bKw)q!Rv)u4X${r7W@G8x zK2oEfRcWO_Q84`;|#q(4?b3|@QkY`grMJkv4b&L$fw7_V_ z9EuaPDwPkNPji{a_g6$|_v7`Efr4O#n(fnyu|oKS&~z?=vYahiCm!fyi=1JV|Jp5E zF#?88|7-kxN{&#I|3E+I%NQo5L!4ls6<#7Uo zu&z%z?EFK{p`6Wl?_YP7X6ThHi)0}}vcZHQB9xuR`&e7AFYntLZDwKvft1|G2^&MA z+F6d)V<;X}%Yh=Q@mLq{{m2yUBysNtzl1QQWq@$QTQ7h#s<-8pMW1eq_cI-+{mB8b zCDx*IxD4r_#5h?)7%r~hnaRM;6z4L_=uf8_JWmW$9K|^4``9uO#;Z{a1^2Etl+;H6 z+otQG-DD~khy9o)!TNMSAlwNWNg7OsDis=>+q<2q`ucHssKJqP0e5kiDt|`Vy(hsW zit%P##U=t04c;vr0&FPlE4nY)b~ZU4!zVxl#T5pl_B%#?^flS4gwDIz%UUCTEw-to zM$6N_{lB;}UPNs~I|a?As&nP@H09m5g!0W6sW@SYS9FGN9BH)vQNAmp8`BA`2O6o11eLn`WCwf^K zc?=b|C)8Co=YDn`I(?LT)kVkZ^u77aDttAOAy3U|yW@~TpbVqLp5z4&U`~f>5_R}R zaTpJtFJ!qiqxSPR!T7r&Wf8Pv6CJ#*X>BYv*3Iknvj|S?H&k%YJ)W-yrv~RCrR|P? z;Tfl~NyOomsFtt4T``(qVYe3%nQ)lO4*+!dP+=Wx2tZlh=~#~y0hSKaTdKgy|NaN~jZ3mpH|T1$|mIBx#UBr+rcs;d`DGD-@wWE*E(5yb(f zKYgo%=CpmZ2Vw?1$PG*!zE1xRIB?WIlIUSRP~EXDz{J1j54v&4XAFxLAs#jmWmt43 zIke!pleoF9()6%dijPe1Z%#x1O~{4+M=(e8b|(9GIgeE|>rDW*S^)cy70U-)qW{*< z8;Yc~bPY4q6QRa0KF{BLU^shO#C^iQSx)C+>lA!~eDUTn3#+BQtH`BVPZ3O#CmxRG z%r%>`(@Va7j`^gmbie8%FoW>H`E34M@E^s>S88sVxu=WhqjX<)MNK8fNQGvKFMYg_ zOxK!79N85}$}NG|#JM&=xHVt~JyAfyn3Tr9&$M)&u0yK}C-!$H(vip(1tKUyi{AWW zn>@7P;!HLA#%q#HRYPx(+^anNOUW-DnAoi5F zJAQpg{$oD`Po$W{*psK?P$Nv&ag5FtUnrb;$E?Gx!}5fAA(rK!CbO#QYe6G|Hngpr zCA*(NSK=9SfOCZ~h@2h2FDZVO+xb+?0)Fp);k*;-ac2@?9OA+b2Mb7m+()_kf-7P& zfp&n^DYLA6S!4SFvZQulu|mQFKDuoL1kOfcI2~``frhT4kR-m zI;LWRy3gDRnm3ypVzUkri(In&DK{eTTr9YzGtIkO)KbRIUtfp1>bgM4_@naf%DV&~ z7KF(U)&*iu7vP8vN&CUIW5sDJvAp9G1M|dVWJD!%NNd)Dz~Ki z@t$!-uGePa(fsd^jfw4%!RI5CWEpBvHfC_e<~qMVv-IN>Y2}1!&vIMfqAxt#G8bh) zr$pJzD~`ub9E>D2PS>}k^cszg(2B?Y-RSx-{1xs!9jYqb_IdgzWlFZ}7wkf*-b*gC zj=5B=bPr5ALbb62K$_$ER7B^MpIfZ(LhJ9%R$AFM=oD|%)8fq7)32U9R2H`7ZR0!b zOqd%v0b^Sz$i=uujN8p>OL_Qj+Dnnl;z4X`EM~@;9COw(lEv$!es}Qx&do4R{Wy`~I=z zztYZ|(MfDy{|axDU!8a^>0#bTuF$kn_0wxE)96eW%Anol5PhH`{&|y01;WUlE?U=5 zdF^FKo#piD%oRHlfG8(;^QWd}BsE0~lh7VTE-nbwA)T3LbLE7dFuA`JyPuzhb(|2?_{Saz5+xd$LVp&h?#72>dZ?wBUu0bO`3~gFw+sR#;ELH+q zETwk9GQ|$rBZ;|Rh5^v!c$|Gr|B@(sp5#{uVm0&G| z3>}UXqw~|nQ+R|uQo>i6vX+X~%-RyZb2Z83dlP@l*A=;tDh829{$4w1Vpv~}qm41Y-`Et`y2uDrk8r!ngOee3nn z{12=J0Z>_oX4j2q*(NIx4@M_kEIUO&&FAVLqV2)o1U}= z1&WD{{vzg|JgvP+Ed&7~FN{wtB>#)f?NF84i@X)dbmx^$DSKvWa+O8Lc&XKTF$;N2 zrsXjEItQ@IDzR8Y_j#@%lPWP8@%us2T(kLDA684qQQLnci1S0S|0W>M2ZCSLPpKh) zre2l;&kFau@V5Ixa)>-8IN3N$^2rUFuBPFgR}9ym)m4xPZ-Z>|?=dgH zCBMV6Cs>#8HlNPTDR4BL_!yiO@+l~NPR&HA&p7M7wD*L(IVux#FksaTJ*N^I9&AH` zRAwi%avNyM80hM~WZ5r>v;#VZ1MH{EGHk$Yz3c67*gokx$0*`92W4PfnNiss5Tm$1 zX-w3F-av!YrUDM$1cF7qrE!T+(@?f7Z|kf;VtQh;RCJo2Mp9Q)F?r75XXa01Nx&Rp zrg7)v*2GOOMeM|ZFo@LkozCW8%lxNQ`7+wwj~8*H^%+HN=1Bkf0I@4M2!po4nKD!} zkVaIym%>d!AC(-F5?kcXiDCd249O!(%Tl_g>ZzBd``qu#-_NeTBnu>IbAWUE<)2vX zhVWBOPQf_V4gb-ugf3FP-|tRGcEsQuxkyNo`5(fsH-FlYx{r&P&}lGm9g#+EezK{{ z+}@La_ktr3{q2mffFvK|4L3n9Rt6|}95Oj=(fo4rHZluW!2>}mWQrFNxZr0)h=F>p zsNj&Am%WXOJtDfCkCGOA$?>sRT~;7|MUZcv7*mzu3VA#PIoTi@E?`6073G>bkUq_vJwyIt%>)LH&x+8P3|asTPrZR7 zIb-}=hncyfxi#Tvx`}n^cXWa<2S<4@u~boJH?Aa6{OMJ)qxL-C5sU4TQSZq^<7?47)U&Hiu1EYsuxvjLig!y(N(R5WIo> zc2=V--Ty>yegNucZ2z9E01+VAkCwta3xkMY+1jIO*Wwo8y!Hr0+xwYx#@2o{FZ2eJ z31~m|U-D$L99@5%IAv$%ZJ`(dKfJ9j_YhDng<1*H%{6_^!b`Qpg*XR8pSeM*a?>sO z77S$8l0f+CR*U8#8e-@R)s1?&FOBqcD@6`FE>N%Q+O&ZzVfHN5z+G#bG;(DTPAGA5)Z4pLI2209mT#*iFLCr>^^&+ zbzHZpgxymjK3iQ{DW=RWYx=XPgb0cc1v*f$FHd+NZ?MY}s%%Mm_NPY!-)R2LSA>hF zV$22{#0Pd-oU(SJyCiDG2c-A=uj8j!kHA|IDF)Nk%@ZnFx+!dLcL+C z!Nc9F({1Cg^=F|^VDgLD3;|rI)@5pa@f33ml-GZqevJ_(+;zc;~`&*Le0EgW%Epa<{$Etwf|=iq%Et($A-+#Sh9r z8`_+}#EyfGN#JHk(?qL%$j~DL>(JC-Xg>44CBTwuq_fpcFGG)kG_^jmMrK_B>*s&Y z*SwyZVe1qxMGPzYIpE{BRHo6<^9Q!pVK*M@2$ilh>Db%p--?GU2ZePb+ z+_cC8eo)Jh&h_g$ND{IhR8IuPp}aOTuNzQ2{G>$k zmXlY^u}g_2{89g_S%QNTnsw0pZ}QRK_c>&K^E9$$Nih;ASZ)gZn5W9)v~ zuJ>h)1Bvhc6Ld(~jso(=&pE7buT}mRhs1*$2Cc4?or7|`kX1huwPNXJ@}N<&MNh)r z?}mN|c`~oP2aiu<|hH45Sx~<7SX) zeM4Gbf5SEYk)l8q-nL~~h%o&2l1~MmhfXXep*e6`NubP*tP}IUPxrlGlNS!Z7w*1* zWcd8|QG~*YAMGf{4KstTbkK@P)Fl(l9M_1*LbJOmB!<3v@F)vRw>`c4NN z(9I!9-%hQEst`F@2-=4iPGU^DwWG2_Vy57`-Qcd`NupfoIOrf`}0xE=$dfmbUCZ*1>IKYGPLhzO;JSpl5syTQ&-YJZpx5P zbMW>sXni39+1IkE!#WY_g=W%9Y$n>dWHv$hw9qyd6U*>hn+=3mkKJ#Uuazuh_;47+ z6+SToU3E_s>`7cqpG=9nf2MnlkYXzV#Ty{avN1f;j1MRkIK`^S5@^ypG-$F zAkkmj6Y=i7F3&JJ)q8?golG|Nu~a_gpwCufVbrT;>{x(Jl=+atsf7)hT$rJ>Rqmpy z`L0oIjPWJ2&vlafPJ@p;rzhPJyXUlbNw5gk&@!)HW0-XCo2d9ry8Z8L7Y&~(6LJ&f zOa!WzxOlc%KDv^S?Dc|blZB>XzIAPuo{cAwrPL^!72zL~zhaQWF$>N_3l_|S53p@Q zlAGlgs~LZu3@Z9i>`_YA6QMBdO2N!W6{Ovr25X5exO!s<)Akk{a%vtCdn!LE?JOR) zIa_=FYpMGjH~y^QAu*|@IjrO3g+$dO>ihH8nz-*BxVqz7p0k|KfV=_GVyK_g8ds5Y*IOT%K<*31>I4xd+oKBd91)le# za!)3)b%8yRS>i>WRl|bfQeR>|)qqra-ol+gYISzaI?2dw{_Q`x zM}}u7rrqu@V)?sfW3rbhst>D7mnE;z5jx9F`8#Y2!Mm4LS6upC$`|@05uR@3V2{S0 zb`tfwX|P3VYJNe1_YLi@12^WLzkeov^qb@`aSagE_LL}+Lv6DI{uaa*wrufgToH!- z%E%E);;1f5W4ZfAC|)baiB%_MrYYpz$hu_h8VT)pB}Kjf~093l`F~_`40f zjH2r>;rf1qR52?YD_{fDtI58Uja3Y7Z>;pMoCq$Qc$D9C2g+7MSXVe`X;zxBGm_Y-|}iGIh< zB?W`1%%$1!;FwE4DdbkoZ9pOWgDBDNAu(JC7v|+Fs*;wH;)rHVNTz}})u3%8L>5fz-0p6v35<0d zKW865R8cW?Nq0txaNl#%5tou2_9Q!GWvGKF#bQn>_T^$HwpC@>hR-jzfWo6I!sJ7m zbhEIM55pHnnk$3R=R@Y8I8demA}kLZne@yC6+;&Mto)$DWtK3cgP;8#z8}eas$m+; z(gTL7E;=<2M1_=tHe6Rs;&0K%SW-d61f%Nu)i^BA9zF;W>j9|GrTlw_wHvUg;3UAA z1qKc`yh95(kle~o18nKaxBsNAT&OGfgE@-@RunPRS~zOK&U^Javp)Tp6L z9~ly>?dpvM)Fht*xP)5%oWeq(jD!hgN2g5)- zzhZ-;Dk!Ug+)^tWDk}$GiuAGz;S%pv>fuzvZ&8XY$s(%$YqJ7jIbs{v7t3iGR*ck+ z`lF#(s|4AkHvlYx5s6@VR<^PoP@r%Mpeq+S3Hd}+^c2gpB;Q2n@a*vGUj38Lx#U2M z^~C)>r8QVgeC&ebM-N|k!!@@YN+JbAE+NQlj@tv>K5TZnw~fD6DPxB*&hzsP$#H2X z5a8@{NCeHU_CaD*e6^EqYu^p#N_6#}H@$E7v0cWrlc$|RUQ?L*GJ{BdwEdPnGobaL z!W(kZGnhZE6;S~hR?_{K?v(PKCVhOXESg+jmCs+BV5zq-Zk`WFW0xYOqAt~bm@XXu zPc0;^sbbD;K1WxiELVq=(2B9pibpn4D-=-kpy&V{bj~8{zLjnhEJ+e(a+|MYHAjG@ zl5Nsg1?jY)y;Evir8G$^)C;6bx-rl-yUIHRLwS~&VQQ46)Mn5ff(z6!FnQzt1Pft10(i;7f*pu$)gY|Vf_{(u8Z>yKGxiuXD(XS zIXU2lL&&8j$<=77wpLg`SKZ>hAAR>@yWiZ#cY_WKE#~ve`e1U`9+wEkuV&gJpm>Ev zpK~%=uG~&YvsU8n)GJ~FbyrD5MXqiPQCx+UHSLrVQ9ZtpY;!PRQcTR2j2x9Fs7U&c zIG=!wE2s5G($rK^&yw^|C)L1S<3%22B}siyFBH4J z@mc~?C7D+fB#H!BWU7iT?=@@M*Z!@4a!@Ed09gAAt&U%C;+e;vb>p?S9BK~@fz1dc zajuujeHlDB#WBNVU=usVI3(?|LA4alBqq8l*e|Aa5f6oS4BOJHl`~5$(d+Jn?!J<;Va=qnKig(kn7er1;S#7xK&G%)5P?XnDd=Ztv>SW!v zXyposmM>80ua`n7cNJd#jiTHhlYZi4uD~GY8rA62XE8;EAtE(y=o*n+-1HtLI#51rISW51qliw)mP*6pgIjlN4bwJ->KF8y9Aenf%0 zunBU&w^QhBLtJyZ=8E$=2R9#5TH<-QTkG>+eZg6T`Acaz&BhcKY}}X6k?p&-P!F;Q zDKS_ln*d!1H_Rih1LyZ}U!va~u!Hx_;_V1|H1+P@nVYB_YQ>-^%y#NFv{7YB94&AS9jIr>V!O2yKK_2--qr?&(sWn%IAO;PwUCNN~%*V3wED^l| zu*Gun3bPf&_j8Zj+l_-c2qo0YDgy3PN~Isv6s}$s z=yWBOK3I%n3NLOB#8~_M_mm(Bmo>o?7hZDXwYSXLS(6HmJ?p8!mjM*%nBDqaZEztw z&J3Jq_HkBW1O}yyh$RQFuv5I_aG^&;ft|3~3pvj3miFQbCM*=`4}M~?yMX*n1uFyio*~6g z9h|jDtZd~P5(~0cDnM{8)+$@s;m@kbw(LE+GbcCd(fyc`U$H~VGK}Li5e|NTF*7=k zFbGN*mNSDp2lil4)#m4LPLq5YRCO&%ZuXl8gZiUaup=mMr)qS?vA-xx z_~FXlLs*W%6&Xf~FLZ@2ln5&U))HD>+P2?++2t?4@c1=HFPd@FfB@h!#+vWWKl!0g zcDoCVpTdA@uS7KoZNQa7kc4rG26Gw}f#T+LeC9x+y5gX-GJ=xexVf;*+oZns4dItc zi!ydCJ+d)1LRMN&#>9I5Zyb#=kIMn%g3BS&Vh2KzH7SjNlw1t6D*+9@I1Dzynk%Bra{tktyJrhVy)zT?3!e(nHc z9RRE?5?2JXZ31+~uI(p4S84);Ss!c75Cq_O@^<|$9BwSsMR`C93a|iZQ!&gD?WDm6 z*#I9+hWS_xTw=R!@v%Su*sc$-259vUF1!5NqsM04Fmt0dW!QGrVywk{@t&V~&*_Ix z2Xu}HH4-Re>7RPsldoJ$V%6X?%~~mtRKuUe$TGr_;EhkD2AwZbB^4Tc?Kxn4>sTvB zf&um+)e1i`;=24Z--wpjqpv(%#Xhvsuqh>4D1={R;gL2Vg-}d$IYmxOZAcJ;XuKl% zl*A*n2#+96A73515`uF3DI6HVxI^^T^bu~G*#X8n09flot6Q(X_||u^f8un@~8jqPZnpo;)Nf9p0&uEH9=Bb zP2+vRXAU}?gpi`$xmeb%PI?7OLTwg=640w|cw*Ly0R{?+gSZ075FT-b29f=8;nd5sO zOT`kwVF9V(ln_a=^Vj`8YY~O`85fv3h9FLhhjN+$^pXxaS^?H9)28B2! zDb$?k+3`FlSd0>lOYrW7C4>2Ao~b1Jpasz6W0#oyDGNN4WM6MXQFx}MP{U3}(oW_n+jTOxOuVM_C}dF6_b*Ah z^3ZyswA;g)iFKy{J-F{ss)g#V->I-b(Y>gVd#Sr10ehx@*Hg;H_-mv)OONfMO_IG3 z$*h^3xbTt#!TSM&RY6=GK5^n{m)v;m&9ipK%t6hLCc0kfnqnqACr=$k5V)`dn^+|S zy7kAP_&9(j-|T=Y_n&_Fv_s3eM2@Gq-a3h;R$fE@RIURSbo#NkLNNKbDmtM! zIJHlL@Ml+PXoBxg*htzTWfd0(9sC1Nfx&A?=7kG|(-^EQSg;({ZnzetPdDp`7mJ=J zQ;8#EauiEmvCj}%loMKHqN4JuA0V`-NJLo{&64p!Pt5y@d!}Y1vj?^Pp#m1?5UqXdWal9J+|x zTy+#gKYGk!AtuC96H0`OfBlF5`kTAoIK~YTUC7P%UHZ(1N6Lz-L#9{zZVv;D)gIn^ z(e2!{I~QHB40ztOCg>LC)~ocb(`;DMKW()Q5Z4MT0p*&-!NBsW471$+52CORPN74d zUV$;;?cWR>w-kG-{+-c^RCFwl0MV6p<8klU+?)=O<|L?ba@}WOI;IR$> zmOl3!JG`_Hy?L1*SAnj~d60cCs|s`llLVT7u`l}3j}9l(b?h^q6&n8 z08^R7+5qBU_0k$$dut#(HK0N^Rb7XUN2tT30+jEvUl3+lgh$qljVpL%2Wg=jJi<*f zL|BRCF0MbNi?c}$(n>8ti_W5ij6)HYGH#{fzNCOfzTA!wIE$KBrP78}p{oozAx}{c za!o*2l3JP;&{gIsYMo$3#LQ5L=*;}go8R@1AN=P-uu^E%Gy`!(E*Qd zm{wU0a-H@tS+mb(XYa~esA&Ovw6vmS==7~i9ux!$i3NGN&Plv)g;iN0^^$cW?e$Dd zY$pJ|#>tU_VyR?`!-NwjQsF`0MEL}U0W;&x#1S+dVbpJB61dT2G?O|BXFObw&3siD2-ET{&IqG)-_>V{2z|P9Ur;;rZSA#`POfp%~l_a_@VDgzf&81z?hBTe$s2G#{Y2+zN zN{j`~NXaO)()Z=7uhNMnhluR7?W?3-oi(!)2kxT>3|3MTeCCbU+}t#6!wpSwUZ60x z*^cBTHxo>2W*@|JpaH8VgRaT}0o%mrTwKOu)E+FzB+Y91L=Qx=R{@Zf-J^bCfTzKPZh zh2kcnzBcO|gwCQ4qr8S&3gDf%in4Mn9O6^ALvhLELSYIt3;dQScFV6A3c`anZ^xm6 z{BogU$pF~{w@R6z{8fop&PXXxE(Fy{#9fsMt!#YBUpJ%|4Ezzq(upCoR)xY(JoG0A z80)DEuoB|xrdfLkVv%PObo} zIHoPcP`#d=LdORYx7ye=>YC~)ri;a?utEpRRR#Mi4=BXStq`xQZHwTjY-6_$@Fp|+(Z=oGDR`C zFSQje7lz4$82!ZM!ZPHtDlSl|L_1Xxhg2JtnC@tIe&(J3_N|}z^8<|almu7_an&^3 zKnZvtDv!|>YBIzS%1t43&!kcFASTH|hOCAji%b!H1xdAVzD_mosN>@S%UG%c7*U1) zyGNM>n9s9I^*aYky?cnnB3J58G?7EX(+Ed!2&A?3^;P;UT1VvWq=v8AIq0>rVxV98 zd6Er*AXE_M0$=4x+qr0bRoN0I6$|U1MlsJqKBBS^2P3u;vn91sEi}GihA0nvRx1PI z3gNQ?y7Dzr8!aq}t9V}?2vTBf0s32P`lLQE#=NzUoJyqJB$F=EJ!CDemw_bn870uKR$=C;L=n}s zF>)!O8Z&L6bFfkclcf*q4c1RhiJF7{$vYT26a&l;Dx=t}JI=97A z-sr&cij2pVcF7t>5V3EW$cxj-Jg&%dz0Z%8dt{P1G~8Tw?HQZBz-Ns9RL?K==yC~;F10Y3{Ek>y% zkgsqkb%RGDJ-c6U^Z<B2D!sUB|f!3fBrLjn0>$ zAZ`O;&s0%prWz++o*)khg`T7xhMhBv%T(d)XAgXtlrRrzxl07VEV4<3ROGXTrGTg? zkC6soO92QWfP_Zq03pTD`-#wrh+P>57lt`J2>pnCF9K#RYDx|Q6F5T&!k&p7K-ot@ zA7HF)pqF!`CfF;kG-X1bYiQ82N1?S9)fivJF7PqBqFCNebxlb{htf&;&@K|HQn*vS zK;G40P1Unc<->gN#}lzyg`YSEYN7brHO#D_ps3C~9lqtjhKf7`_l=#a%8K=>{X%$W zVPy#YePLE5y&CkOtZ~}$s(tJ~;z{TE;8MAv63c%{#6Bttu+j&DoI>-hlw6&(X;#sR zltSq`1(JEU%P-%v=-E~%<*kBhf>Iv#7?NjO3WwUApLx@7yzzm*{M-RlTJP`d)nN5v z_L~M51|MBf30m#$AHmw;<1V30=O#(ZOIXR6gJ!NFt^2f_qVcedv%TBj;Od8!9zDkzD$yHIe?kEONR-r_N*ja%=vyP_r%J*ta@%B6LR(QA*Gkn8^X-NB?-Ztvj{50FdLJV zhHR`5AuBA&HlsYjkOoQv{J4eTCrmkM>=oUX(YFQZTr#!ER`0S7D_P$>RO z#y3kZ$C|3u5zduZB89C=N-QMm;vTHP{Q_YYo{VVmnV2j^$5_iTz7k}qCFJBAwg+OY z^RGnM*90$Kx-g`DJyo9U0A0nsJY;+8>W!}otwYQ%vH^WSYuy>c+iR+APAoK;SG5XK zjf9I)EoGtxt60~g8h?j1+7FA&2bZSmk!ovI%GRmSP26w(?#Rp7p~{%WSV}eNAP^|9 zdekFr$pkClXz?8ud!@nfJjCcek;#wA$@iEQ2f0wJh?Q9J#_aQB=oU+;98gH9u*tc( z%9jW#0^+J48bP2|$-G1hxc0cx%_Xq7O3UGW*MorHCRlbNhdvJv8z~Ti?`S(x!7@zaAjDzPCRD8eMy!ev=kz$vK(H} zXktdNm~2EL&Q`u~UeL}QcCQ5kA&@lr0dz!6o5L70{zn&H`iVHiU+=Q zfU(YTfW^ee4xhMqMO-b#?2Jo$3{EBKsnW$yM7)9V6*fxn@C`a|va>I)2{NndAFOQj zqpDs%LhkD1IHQ)`-iyNFB2KENo#22~N-1+8uO^?up{g9SDm3+#PVO5~N7b9GfsN{5 zB~6mPk<+_CPUB}KYh~PE@t2drb`z@2jL(1SFzuPM7M!>yh>5O1{9e;S7*ko!V<8V? zNgbkEtsL|rD%m(5O+hOUlZ9P@2G&S5ai!ma+OJ~Qkdj?YFQKQk#~hnbz;8h$+W)t0 z+Uu@-`N!`6<3~J%nEvBAB||l!r4{a{-i{I zrr<{hH@2!Svu~ueGLQ9YlCER*QuaZe3@dZnvVtm7##Cfd4sCX!(L9;K#nDTNBcBpr z<%RYeJT1tor2kQ*(k@EqLk|Nc!fT>_L-#XJbEfmynxOAe&O-+sJ@wGYMC*vPrJ( z7FGE*kQ;LpeMS8$c0j2Tw0Q6+D|L!s!Y#)AtGIHPSN+%n=GMMp6XL3#yHb_J>f12M z>wxRXu*eZqk>e4C3VehdAIjbhROO77uu!1ZsrD<1z(WPi3Z0ge_$~7It?}qJ%rCw2 zG0-BCa#VhJ$&?rt8Y5AbY|y}8S;&$XdPS8BI2RgDL$}?k9@YOO}6Ta4vw2V zQ>YOssB}3jK0<`Fi3W=bjr3G92(3YE>zI_{l)ZkmMK{0i^B?JC4nKmXnZ5XVFK2G! zp_q9URgN?J*3dsuYzi7{)i>5Zxn;`rk_0P15wE&eCBTBn5^PTOK3HMeI#F5oiEolB zPTE|v)Vftz$hPtl3@OUP$ybxgtxDbvjtvC$b_f-5I@ss| zA){?lo;p$g5P1`PA7~;em z3SC3X=!&9b8?HfhM!dD2*rH-z)hW(|Q=+Gb7&;m-upFwz^2VR}R_SI!0^oyVW->iNDMz-H_6KKOJwiT@jJuG3hEq4DQ z0ZG!1&GHDN!lb-A7vNu~b#3TXD8?5tU5DSokH0H?c8-6eksm z|584t=dGnc5<(u?|DzPY@vY@s2^g~sZoD+7OCIx24oBi+~0z(>Sx%APpZ!{(J1V9Va5@^pa?n^lhZT zpQeFL*%v6ifLre?xTNwaJKPup+=ew+1hrODA;U^}7%b{iu2d%AFL9#EXD*)GR=<@n zc@iu0s?@LS>#?BCl^9Kxwb25HPRv$h5*x>F#jI&hTzH~+xH%xPYW|*34c5|`{o)HR zzU8{xu6Xv9vt|~fE3QRXPP3s$HxVqYRXHa3J7rwD!}qE6?hJ94V!1`J=;0nJNZh8X zdJR@g2~_47RyZ|y>{qb^k>YR_SW4?;@#TjiJ3Q|P`Va=^0irVHp5Zaq;`}8 zQYS(uWB-8TL)eojzRQ%|MWDyZ_v`AhNK355HE}3SK%8SP%*}OIy!^ofG1l1zuzE53 zZA*izhB>CdEm&}kub3yItH?W)eBUWDdJD%lp|*dJiXKX@eBBE#JNCw3dE3t6ozMQq z`?}plQ6paxQEeE@Lu=~0`m+O!Rs3t7Fyd;dxSF-IzEM!S6-P2p zudGMVmVGL^vNvj6mecEYsb`^*NmVBAr>+7 zPmhwiu%(XSV$zF+C5!1@uETvy@Ayyrm-3D4%$-kWy%ZoURlcv1m#3m;z2j{clqNBi{v7 z0(c}ve3r*iD>pA9EeICgKJtG8%>JavJuPP3z4)ofTDuGeH6K;V)j`(BPVGq zx;T|q;CS_bOj<7o8o{fQ_m#I5eyJ8waV?^n3Z8hg6*dM{AWPVLY}aR_!q)2uG1ntD zoekW{{8L}}$fBEr_YQRPf|KSXU=3xNHE`tkMJsegG&?%mJGpDxz_7AA*+qUNt)rs@Ns4qN@Y>JAl*86i(0NO_Aa6* zmKVJy$Z{s5Lq~R|qV}5#(j6_i00lZt$&)sN1gds*K*SN<3*k8GVPe^6ZPT8(@Ztl( zd+@hk8Z4!_B3R~&N>%K&zB|=3U>s_)Xli#I*qH`yTLm+S3Nx)*6%-YNoT)a)(6Oqb zl2VG4ccOU(ZH)$drLAaWXNj6>eB9*0IO)^Ic!%~%tSYjq4;3b&=mC|onaK+xlu5UFPMIE* z(IvVal2G2BSHUa@Lrkz4K_K;Owd9psN&X&{V$6uQs78-PVK7cgU)5wt6JW*6Y7O&s zFMQd780!fw5h}%1!wn-sr~q+jGjtVJvI2Grh3$mY7WuZJc&jM^v1@TulOAurayWkx(;h*__Z3TLCYAVoHAG%bv2isZt>8 zf||K_Wc5TT3Ard%U%C3H%_@TuiMmX&QUnyY#45um)#YSz9~oV~MF|CXmi-C8oJgML|3DWdmw)*CF}g)3EC23a!do_~r7$vz^!9@uOEg z`#_Af{{U-%R)cjE5lNx z)i5h8C0Uo&QcBg3u!2WS2v;SqB6QV+=&I3*E}+~}wAT0&Ggyiv{3I^XHICNfqyn6x zMEa&aEDPhZ1#D)LLn}hiit>8~B*xO73xbF!E8+4x6W5R5Ne&tepLW^|%^Sy|)XA*^ zKIQ^>x(+u|01$0F)G(PZTr()=hODYkSG$*HwtB`UfX%!W2_z6@;7xi`(I`Y9ZGos>zjSu?Z(OGAv07QU#gUin$WG@1c+M zwxV2zbt||n6aIYkl4Ezj{Vj(txIpQ&z_-!5dcs5D#7n0uV~f!m=~W)mOx}h-l7;+5 zsakzSsho>^mr+6jtRZ`Mu>K^Z>rxW~0<2+WcBa9}r@$)*QBH7G_L!(pyi>nf_R3|> z0~rAC3ZrU)j`t1CGlPLPEB#tJ;wlNqTxSp*R9!F{?XUV0c#WdF9+mx^(y>^xC3+g; z^zs^CkOQ6VUGV)UvQAYI)Y!H(y~9ZjRF%FBD8O)9HcprxmLIr}?jwVh)dXiV2VKGI zL_ySU3xC!8liyv)_1qFyuNff{zg>|dscH?Pnn-=E&6#b9$CAmAOF-k>zBCw0G@vBK zL5_zAHMtb?Tg*p~KW)kT8u}_P#_6k0P#3ckC^ApmsP5@998L6AFh>w0k~ zV4B?q2{y%t5v&Z7qO5dx*x02X)C7B;Ng6|6HAD?@OVlzoOf6A6EJ^+?*BlNr5~+8X@PdMyg42#tZ%gP)#tcKLVOcz_|j@jcP+ZAYi9fG^7CjY-~{^ zhJ2f@@eyY~3a`#cuv<{omnm~XL9~{L%czl~*23x6fW9(LU0JJj+q8Gye)m<+e#t?h zQ2lKxa$gcxx4iVWD=xpHZQB87SS%Ggr;|+#_E1jj+s-~aMUiDZ@Bl^XDVLyPA04vq z$PlRMy`V!!^ff8Uu+9q}(REnl9(cZEB|b2kM?tyP%928zduZLh=H`oTe&0j)&R4dg zkxP3>f^sp$>FA}$-}o=ywzIRNu4dB710Vj({N$b|*bl|#gmVLZQ~TJ5eQF4x*LL9e zi*6`#r-DOi-It_j5E>j$&YkEJnktpC^WYJhz$xxw7We3ilNulMh_|>&H>@ZKHaU`G zCf4rFuAJQbS1Jqifz6wLLzO+LYC4tx^Leg#f~O~Vki75*b?&M_Z-9%y{aV)*rOFSA zeFSh}$;C9=i|-Ln6E*Jx#=8EBmpyPGc;6O-WyS1K)vv@JO+r^(Nmf;q>Zn>xRYF%( zLRSTfWmDsZwP>o%S6-tyNYhJ)r5Yj-WmA1w!rw(F-F;uUcW>{^qzwy9m3fIn`l_9^ zue|%!v!jR9P|8l)d*e)zip%G*2+~np=CORrs(xE47?w&TpGui95x}ldEWi?1=Puq` z<6PuEXp>V1Q5G@W6(F$`chwTL%xzCx&4^o~8PSZXUH#+X-;8KCo%6girP=4B@}$n)obQAb6o5?G-p6A zgD|0-8|%+IjPAniU2O+D2gNIb!#5I5xECiKz2x{C{`p&0&4Wtw!hIv5pdVPS_#vpf zDyX0+l3qc-BrCn58aFI*v|J=2jMP^`W_jjcl`EovsB3&XWAbz0fQ1EE#zSiyBoAgr z^1eZC75(GHoq;vW#X+E#|NT$TY%5eEl(+qfFeM8!9g;XV29dFo{{SIuy+Z7X1#ePH zN-RP;DsA{=Z-k5ympJ*>!a`b2m98LYKx1m9hZVufCxPqxLDWEtCXrezkcg3)t>Dn; zwrP(aJrHAUz+f$Ff_J>^Rj<6}rjfYfu$c(wC7NQE8X zw@GVZ4Y1paW^0p$n`GutEW;|(RWj-EqT9Rgp?lNL!EKqyo8Ji^J!a^uXm+JFBp|fb zYTS!xC!GnCEtg^!VM#z1TCq#?c5pADWR?vPnkV_OW=Bm8izRw&$)! zwuz~wLC|VLpHp<^{7b%X4J|n>N;oa<_oAt+9^^@5&9r2&ZwP+!36K+Q!XExSBPAAL zlFW`Vl40&H^4D>vA^6a969;ivt7KcGR3jUQ;E-XeJrJ&MxbTb>(=>lXYZ0<+04^B5 zIhoL`Y45!K4F_VZbpTdh6MV)ESKoN3Jv1Ic1}UK$+;FUU2ezU#A-l$cGm1B__r7wd zrn18p`xj)-2cazA=NjBo-MYvd@mm{L45bco39c+e*hMI`D6DxP(tYZod-sOSL77nU z^IsIW?C2%O-tf=fGJ0Q;R=9Wb1&~*V$dV`HBzy|qZfTEDA+C`9NBwEl62RBXM{F= zfbLfA=~P=Sq#-=WB?}AZ1uHRxfSp*s=Hc0)1B^8pU@`HxjKtL;1YFp~Ivl1oJI!=v zgo1Co8r&zVieZH=O!N)6iuPB<0;2!k$}NQ9-b)Z{?MmXd76zcIv>LiUoA4Lm09nUY zw1%=W;u7W~$DXz_zCz^GV!rqfZ~M*p$-T)~hiID;sI|700AnF4O9F^i_6Qw6yu9<> zYj5-^s~D^Kgq;1LS%e%1QO#g6w@RI#c-{9TRxhIuh%3lrF{d;b6EE4aS>KJA^e5(Q z7I|!zp9Wn5QtTqGlzakCS_U6$n^3gte1UnAokK)e1pQ_`gd`T3VQ57fT4mTMB*PGv zQtnn_X)w^cEe@@Ph4W(6b&$tWx-O!X80!tsyZCtr0BcrpZCEO&qA3$urU6o9YBj({b_H0Ouhod6cr+4YS%Mj* zz7if91DVnXjj90>p$;)LOn{F> z7FpU-OTBAz5L^!}qplM{Zv<)JRx+;xF;6OcbR?wg> zvgo?Sbf&Z7bgy1os;_+dYVXYM2jBL8%}?%b&;z(G7d35L=!Ui4t|b|=tiBR3a-rrZ z{$mu%J>s2RBsN{fyec`5WqpWd%^)x*|Ni*? zSOn-gM26+3R`Ei#N8VYzU884-15>}UwHWy zv!-pB6|Avt>S5p@zTt`^1uaAhb8m{?S7<25f0VtQkc|&4+Fy}95}xA&``L!PUZ7!N zU-f2JAk?_%r^U#@*v`)Q(_+d%Y2uh!!%x@1c**7khOfOTmpq+!)J5 zboA2W|I<6(zRY#DQqskI@xg!g{?mW?I8hqUbCDzDq|P{1>0S3EWbu1u|%S4LJ$D=idpLkZ}~_^JUF(Fm=mzB1UO zD22?_m`TlXaLFR=@|DW10KsD_N2%hcOtRq%kob3Q!7Y*PhDrMCLpeExcbIf zJ8MS-3NQYkBM-4j%!NvnlZu0%R(oF+y&)!&6SW)|M}a^kZi(xer0trs7Y5;JL|0wK zkfQTfz&S}RQ$=2z403Ppq`U8-kB-{*dRl6YwFml2Fn!g{7kj66Q`v{d2C=y$l)0$M z99Wg~$_YrRaIPYXO{WA?S>)@w5~4}q^u*|LNQvb|%>+r)a^#Vz+H)Sua#x&TH;ArW zMAhVv)jznJBE#5FSxl>b!(JK2*u~T&DyC*mV!tm+3-7DTQ=Ui)*6kw}Nx3nX4`Y>4 z4Q8c+7bEJ62@aBB8blgpHU|hbj%s%%_|qfoGOv~U3FH-5kdA3E7eYzcMQC86(Baw6 zowwihyc5rR$}(6pNL<};^~&H1m;A!0X4pU%-`~4Pg-aNyBKL;YbjE^4x4(b>_-_9GYQhuiR*S?>#`B3c+nkDqSc9$!Yn~ELBhggJKv@~}Rrb39xrB)@ zD|NHNDlR>U)`#&RvEr4^iz$u<_CU8@6HC6#Iy-h)iKagmDq7Xb%C>TZ63?1{8KDU>3x2$p#z z=nXkhjS4EYVRB&c$kxJ&^U^s;W5ffFa`o_C167unTDXcSeB>!A#%gln>exVB<><=2 zjA3+DW~wGsTWZNFRHO?Buc5(;n2BRN1O{QAL>)~{A>5WD{5~M9k|TqZ?9MArzJ^+# z;PBo__sNIu-COK+BI&;pEwv8&ee~jEKl%%AyPV7nAnRJhnv^M z_EGLHSh2ZCXu^)!rfiLZs7uc+ZT!pGw3;@+e$u&$%UVFVjG%~NWF@z5&%6(sq!LG* zRy2*f6(F&ioR@0c5@FLsuPPF2EJ2Q6n$(mlwU{^Yg_23KAf1za6_prkiXJQRMde_% zOnX!`bEixth9{t&i6x41yVh)joERe{8WWS8BSCUbwt`z~^!c;NehskJ>D33aroy&q z?!4{Jt1iFlsVT;q(W+1IqN9h8e&Bon)bha_KW-b4g7&LJdwm=K6?D3nd%zR^ws-%`U+A$MJ9>X>H(xl4(w^aow88R z8!1&+V|wh-1o9Ca6Lz-;FdL-zg15bdqI}rG$wXF|kX@K^W zHexI`B$fhOluPpbFF#YtOZjwC{w`K+fy<4#88=^%SWWU@1wNp!I^!0)>KhfAvhp-; zOqr_tlvQCsMf{+CK*GI~oV5cQ)@nr&37$DYM|JqtKue>YEjg1{-yTC>Ih0$!=FV*A zuG{ar=XdV;_+Nbdsm5c?RyDzw+|aa5!)<|-;tHmUE3j12`(Q%t&kf_5h`lPs^_2+H zLxAgWNa1Sh8nsUpRP8sX71b62L~DPQ zi`)GEOx+czSC3wN?C!TF)(ob;*DV%%r}n@kh4vX^tzPPvRixZ3Swp{ED4FAqlaoVK zVb(CZH1uW-nF&Dvvxdr`#*sY@$VIqj2T9>y_TH8cmNmijzoVWBhg_%xW`+zCW7mfC zu_W((sQm@D2?L^HgRxjxs9o-Pu)Wn73jqWS9ZWS!7rZtjiA|V{J=>^iW;Mcd0(pAu z$gzIjukI10#uET1G%H3 zpFR9%Pc4A;jEkP}oiDjzHk*}As_IZh1-hzsa0oEaH2h?904Mr2FdaQ{^yDL_6g54$ zq*%6MFI$p6VJ=yQ#O7++Nn&|O0CXnMR}y;V*JH2br3()od1Ut+>KuJKNz1W+r@Orm ze&J7p_qfx{zVkU(r7V{a)zDXF&S3^3Z5d<5n+xd}z8Zv-Z>I%MgT2?dl9FAeK#dR| zwiIS@#(ND|5l~{ez)Qomg2+r*c=WsKUjp2f4B<1qCc`^%qzsnTq>s&pOt|AVc`Et0 zit*(eDEFkNzPLXKBF(TN6Ul(CWaP0V)qDwQJyxd60k~?4!3rRWCvjsFaaA;+0fvV6 zav84*do3)J#pdjr%$P4!lMg zBQfjS9swZs_TmOZm!faXP#ks?nukX&KJw%L^G|&8-cNADJ4bhRjx7HjK6GSf__wos zaCGO;1&8Km_8ev|LXC_?{dw~`x#^3@J;4ne-JS;g650BPs>p)wLNW~wvKRgH2IuDEUh!<2#}J7B1Y*diTgc{D+N%7W&D ztQ?#aYD=oA!HJ*>QyXQGnT)IAs#Rr4Fcgz8H`l*l>Dus>r;cmGtZ5pbg@PXEo1iP$ zKE_-sUKJyvYk994bY(8s^KSmfU;5K_*8bQ#-&l3I-J8GT?zf-%=4n6_ac2B>`jL|( z`^ppeq{;%^ChM2@E#ZR!n;S_y!&wJ;O z??zpLn1!-T595;Jlo3#^&`PhewmWeR-&n%4w4RmE);C0vvBSP-j;6bTz7nnFXmbwdehA%RpRg=Ic6 z9fd9zfeaka&K{!4fN;^}Xmrl|T# zryn_WHWdojfMT2I+@%69{RaMjYzgvx#miPk-bgCtoD5sih%k-t%~Kb#=26_gouS4nY@ z>too{EN7MO>=g*i;vsRQ_u@@M5@QHgl?988JF`P~-S+x> z{{1_iA{cAN?q!K_VF{cQD(w}h?BL)kk}4fR3n7N}&>=-vkDhsyh`#olZ33%xh?xeBN zN@$-n0A@tjs%a_^cu^+C*CKa^TFWsO=go?gH>fg|!YM&cy%*S=+c$XUIp)L>z}jLh z|8)RBWSsyqg2sM?CV79H5p|RvaD}v_Qo3H?dx14(s2()=}BG zLxm{=yLJX=U7UER&h2f}o;Z5qso;#&6wnn{n2U0KQ=!3S7(4-_G?@&W$fem)x~@Aj zQS7{bf9q0KQOR`cF*}ARA{^&ZJroql`pyngw_03`klFsou+qOpxA@xEzm_-p(B7Hd z-@E5Mdnb3p*;ZgIa35cVvC4yp1AGi)k)HHDpr&3HJ;?Z6^jI=1(<`r5H!EilPXr=1 z$)a=8i(DmKn0N@OubtV>UAMmeyvJBg9mIi&&MI8^yIx^wrdb62mVd?|LM;sx;_10^Q^6psWiI9pNWT_r8<~&lh{9 zI7pQktDb_2FqVMRb^#uh2T68o2r9uNhNK#JQzChenqtK~HcOl=orD-0S*eWo@-S9H zVkPS=6Onl`jvufN=?B^gcRYFdVghav}S&$P4@4!wmHC8R4O*;r zN^zYU#HsX^B~vaq&gyL*FiP}N;$F`Lk$f(jrRtA@-W`#tLz3kAFPRKW)kHGFpvH{} zHykPlhs0bg&DHVLR7xPP9JBNT$~+?oSKL|iSi*X`S|P_0;3SV@k{C|u3`t#c^lsPk zaKm_301^pe<%Roi5Rhu=YCxdtDt=EYR9UbrXxzEH9}7TBZnaTL1p&ln$?{}-o{O=X z3glGIZ>GQ^i(P5_TERG5BCF03bX9|X%Jj(kAWUf6J`UU1+{JmNZDTHET)*Y!X@`zp zymS2Go#U4z|1RD+wk>As{?Fh4n%Av#?%jNmcMf`!4B$~M>m$f)kS4+-(Ibh8Y9^&l za?5!S&y3zn-0R|Jz4?{qen1cH@U9Iy($H_r-kifnR#}{N!%kp^&`azZ7>v zZeWg-j#xHM*?`JUhLn+2Z_#OE{Kk|VV~91{h3Z`8B$2Qx_!(J0C?nHl{B!zhZi|%~ zXr&XkZW@wD&7>Zl`K>`5`Bc1R$o$rg>8Ogjv>)uSh*c-v)bCWmR24y65wWdhh{Y99Z@!p+ z=(7>J%Djut5){R?2x$ss#p`S+Ri0xTR1&jmSnLfNzdE$-dpUzYJVB`R7M@%tRdRsbN+BmU7 z%^Y9zy-OGfO`gpWP+xKOegs-Hg@D6UrKN(RFAX<=DU_o0o~j{HHeg^km0%7LXXOB0 z_4vsRaxNT%zzs{hS-DH95A|w=CC2*c_rK}{EiPr_@IHed{~w{+AE`nO4_Pavuxvv!b@T{UkCBI z;7D4Gg%%cXWmJ(yMdf7Ji}L&&6RI;@KsmplNcS&4{BIuHJ9$B~v)EhA zPwiGyK6T}}X=E7rq?aL?lDe)g^9AG?S!?(Z7zG&7BcEh2P1lC&L0XRMG7!icrvfT8XZn+`plba19hy(HOYCw-%Sq{#>D~ zs>Q?Zt$%q(mN+4?!t0b5rwX%tHJHVHeD0#Fty4{#jVaqS%96rX>zcp2zwWxeVwvoE zDwQXi9x-#%Hf_sIuWc{2H&(fylW{)}PW=)R1QqW~yvg~#Q^x8b@m;T+Rc6p7dVbh? zw8j=o4X8%TBta@Ir$5w^O>0;tk{Qnna$U;t)5R^&Up*Snaa)z-Bz zHTT%rsWmd$^5b`we>=0CyKeoF=biW~=M96^DEL#T(1BV{WsZ$m5>ZnC6BX+?Q&sTi z9J*SgAh@r-S^J~J5@-ZfG~$X-0^YWKBWi#D>ZHSEtX4apg0TW=hxIyaBsRB6k{p;A!IWoxBBNK+)0uH?YiXqzE=IaPC!*mQ#p27 z#;{bV5Y8j|_{Zoi2shOD&>BsF^^4&-m?f&W+Okev`8wDQ%SPYW%tW-WigdzZK5f&S zPtI5k22%dQGS9>`H7k1Erb3=*pmh#&SG?8Lri>%o4u`HsB>Q^ALPlrW(}?=6Hk~44 zeZC+3)1+~z6y98AVTG~<385k?=H7@otb#zB<;JWnR+N^5ZLu^!lN@~ZQxX}HqEn0` zqDibQ{>3~M2UOXAgdQkNHj-hv_o=9N?|sju;^!u;PZ##o5o8%zHb@+{$z!LC^Mk0b zVNKj&ksFhW%p5=55GxEks|5_LOt!SW)eQKj3K!D~3)32MtBw*&nJr?HwrTIY<%h3& z){D;%jMW%+h+WQ@%Z3ZJ<;5u;T2pI_ak26otqfjp(FG7qu3^ovZE1Y5Xee9mH1zYZ z;1z<&FLgzZ#&tXtc!RP5#tM}UTfC1>>m{Mv=2kF=u zVm3>s<)mJvlrlz=*t#e7?vmgzk(>`ym++Lt3UL)VD|nAna!b@+0-zHZ&p z%=zKsZ)O2%7*i>OB=Hh*DiRMRwU!OZDzG5fOEKIKext2Hw6N>c0C9{2#R1>N)J6(| z+?zF7F@K5rrY#GF=Lg1Ws?_MjjJy&!L{3x=;eq=4%2FWgP2=LV{9Hs=v)SzX-}1T( zDiR5|$o5Z0W8wi9eCA;vuS{l-%$?A>Z?|s0lVbj+y@e8*DO~y12SznlTrs+3W9d$@ z8@s7I63|hJucH!*D~1+D87UQM@fKpUaso_p-IpZS$$$NkHPNL$H5mR=;8a$ZgB3%g(=hYsPha{I#p6IGHbHBt+6@snC{0tw`mfZH zReGKwuA=I2!{jSwQOH!A_$YpdiXDX;a92SNG?hK#mAlB!Z0D|9fA~BQV>LxV6$KSK zuqBtNsT$IyT>GoA=Qdz%u|ikhIJZ8e`F#Gbe*9mYezex6K_Sh2wavnFqRyB40-_5P zN}+@``*Lvxmv_CssH?;<6^^HD4veN?EDMY*U4$qEpL|k!afOO#5|uS>At7k9im^Pi zC$CXTr0P`~d@+n#o|}{F=0^=yj96GeiA^dI>JdZox79?DsppjqztMILO-s(5i!2k6CE?TMqu&vg>3slY(Iaj3I$#Kvq>Xlui@0*EZ69GU`O9tCLk zr?l$1j!G;yMwwjQZqi?zqdrHYn(s?|dS$P9V8mD>((3mr)FqS)R;-Wzy48O*s0!dz zhaPvB(*iOt(jqXS?6Klz@Rz{=0)ZG_bA_fuGXDfqP*p|$N=8#f;H^NF8A%-c^^4Cl z`9znHBX}*M4wU%e0&)p1%5(AAQHEM^9BEo;83`g;&;8G$s=vV3koqeJf&&SGa!H zTO_g8s5wr-SX1bt^)Xh^ib&vGZ;EjsOd})4aK(`PH!JqCCr~o4TP8(3=q5OnI(y4W zZ(FLeO7vJVzSJd+aGe;$DHO`sA;uDtj=C=4u{uL*3Bq9=5XDD4mJ&`$NXba@3xWkl zDz*?NUxNhXIbTr8m?d8(B?hsLn>gpVX#vvk@RWpE5eJ5$6_@Ya63nD!7*bc=GrYb4 zdMU$d5}fv9SZZXB!O;3?hOruKTin$$E$4~HSf{^v zTG3aPh-yFB7!QF>RI-DL15cRRwp$TPoKU6r6;G5&$yD?Ppw9N;T8@eYqoSbVAQHMe z0u&`gCI2c*QK*dtF(1lg%alPy%%ys~^oyagNNmAW@^>wX<||V?btIigQYg&JLzt0* z#8sD|Dh%369rePwRdMCns@Wx-%@4pNlD9D%omseW)HaVrj(ULG16ii3SQn|rS7<%vuIYi@|mB-?Zf9N7EhzKKE01fp$h2w=r%+1yMx zU*_r#!lVgIFHgK7oCcj5uBXApy}i5n=8o}#$vm^&E7y1xfVj-O$+8rO6hjnBxJ)t> zVPO&CPK237q+{ac!Xe6;a;J2L2)OI-fs+Tq#7vw&U40pPs|0<9PW`zOUJz1QB#|AL zdhA*`C<&jb+`8yLx^V&k&)jBII?<)Ar6m{m8e5hkbi-#qab1Nc<>wGE<2P1e78QMv6XuyohAJ7`R zXWW>1dkG)msP8Eg^pI(c)P|8J&B#lDDtwh8;;K)UB2{dh^v& zRoYX;l^7nYqb@HRcBW$3b(jgGgyo{nsT-1U@XEhtshPbPw4)3u#Bikp-qW0ugJk-H zZaW7{zd`{%TkelEBFP9%btB4(v`vF6%o3GOAwD*exRs*0-IDg~EmdW`yfN50hVEoQB)XD5w+|S}~;PuaoZgKl}fjyXcA%dHWiO zY6JC^Y{NIJ=$u{$iGl)u;a+u0@vca}t$~WXr&0`^MU}+0q+Q~PW&%Gg?Pw6!8eH?L z6u5wPKLV~lS+UKp7-s&RYTlHIs6cam=sqf96z<3ti&oV|F*!S<%V_G?kYg-aVl3;TrQM4{X6PJ)AUtVDNK)|05~qz`y@fH)?AMA&f@7`s!6b28aJj#)!y*@bqk9VJ}%QAs8PY``S1 zmW`S<$(!G(>!t2fFqWdAa%EL#9;6tnLmI3uWws0@7C4vU%3xM)%o7+BComwx5{=5- zG#K0UN#uv*h(=V%%^rLYee0~TouR@4UU#{=#25N0)M?(!qs9LC>SS9Rap&07kR8E;FGAPZwo1}rdJrb7Rm+emwuJ$9RFL=SG;}tYBR_s>(W|| z?{i2Rmod#0*tg;OG7)@k;#uYFCX84vG5LBRiLjD(*WGk-+gQt{6fW)7Y#b z|9@08lxgmxl1)Z(xRn(T6CO*lAiW+}Woq{TW2OHTVCrffEL;++L)Av<%C4lMN`#Ww z*5_y|V^Bo}Ws6}cb1W-C_gzWM@&vx#-`E}1b{+x5@B0lqB6X@Ne34RK`;T27`uu5=Q zMV9k9kF2&xU)9n-(`$k?+HWa=@46Ml&sGxuxp;kvKsL@bZ>vH%UOFbRxL)bc9bI{H zF64rIp3>>*^?e2XfI*nqKek3Kj&%2#>bzIFCz}Z8lT0^}M9(3#nhOxBuLSy(e+KvDc+7EkQ-fy#>kMufRUR`iF-K3w4jOL5#J@HDyJ4oGo9Xc^DmG8?rNjnTb%7(d* z$#DLy#O&$;g(7zg`i+?a=UPHb-1f&T2BQ#)!h7HSLL`Cz?ke zI#&emjmIZddJauUw(&=r0xUkNExi9y?d^^DkbN1=>sId>T&^=;=Mp&5t@;9PM9=hBBtc+ z4sS3;Z<1C`(c#a8agvjT4jrU9Dk0>>P@`B(oc2YgxNYS>-@HL!Rtga$jR?}Z{;~R%x_%{5 z*hVc!o}GHGs<)kkQeDVT{Y%BQozjz$(7C-3yOZI;NLddT6fL#tlwL>_re#>7*erA7 zD&qA0iMpk)=__N! zoSYUw1fsC|9;>o6;e;X-1$lF4ml8qEi78J88M`r;nEOaAwNcg>YxBOs96UBRzUXfpX)o9&%6LN3au)OX z(!Ko&pszMSQ!;hGg{{`C5{4^OkywQj3W82st0dM9Sf~|NLZYx(AQH4G0q1Qo_FNDM z#y0T;+5=6qqF^frho(vi530g&f=u3FWMNt{q|6aM;YI|f=yXZBFhN%YFqSPBN+3%& zY?kNL&~;fWVrL!RyUZbi@@O4PWu=RZOGRRBFM_ZxYCa&J&HC2Ik}dt0LMtjK^V2fSKAK$8%!_bNKAnJ~!{S28kYhHS6KmV1-zAD?%5bw+1tb#F`gs!Lt zT^06$$*K~%+Na7`zQ<=1-}=B8f9G$1FY|;}fU4s_B-{wMpE-8rO1A|u#HI|0)jn=^s!1SHihxtb1<*PNL4JA(eip{+5G=;^!u zR(*{^#N+tmR?F0!Bo#7$bI--V{L^|eXDSJ=4E>}0?yxa$gP|HITqxy7Y1Ymuy z;+T_@VZ!uQU%;2tV4LKDhEUEzA?a0GnE*?%Stc0JfmQ)zk_tps*Ev!^C1tfF+PODN zDdHEY##IhX;fjXFm~--(eArY-PG=&(bLECX53!MF_M*4lneE(p^AG*v@BQ+}zj)tS z+f+CU*sD#Woy%KQr|y)D9wsLqu7$4tN;`Y))1R|LJpbZXi~9=oVi_*%xFsUt*{7fW z?F;7ak<;wIf#8krORaEwAS}H~PjV^anjL}HrIS`F7 zEz#AU(@+2Qj+ux%u9C#npoAyb7RzJHs;6P$!32236(km|Lt=%jRaHFmG-6C7 zk2B84)Y#0yLs<&4#aB7?!#-})HdraJ%B`mW;gl&?4dJB6wS1X{P+4pXmL#H%r0L_GJL0CNvbqFg3THyF<4)J;RYgD_K&bn1ZLMhcGlTMEL9MAf#N2! zvu9rr6G@H;YLXZ$(#k@dU|61}7)z(mbz?I4T{&+g5O`@lmi~3bA}9E1qK`7482fL( zFk7ogqd??0NkQm;d*KZtBWr2B3pEo?J`E(=8+BSnx~7C%oanjyKnu3;BlxeBn%%4+ zdk!1~0W2D|QmfSJp&hd4t2#|q<&Tq&27zsZG~hRcx2n*U%dk{HF)8dtC;(bP$r8>% zs)(_QGGe(IfPB7hgiy|Q-t;66H)Dhjt7M~+>m!<9aaxw-!N*F5m$2fy<8*UmyQ zR(tL9uig^6B42goYvDOWY3MxZCgh;GZ4R)O=;{Zb{;h|brqVLZbpq#Bxapdm&wR^E zzVftZE}B-D#atHIKRf}z@~%MDV8tBE62glF>F5gqFU$nB$y}Z@JKq6DzG?Mp_if@f z_GQ-lO1F<&Yztx zAiJL`fY_joidR=M1G91flr2eNmS{w!JIDliXasCl@9Bl1l@4bd4OdmyC4h<|fqa?bwI2=mx%ph^e#B0+&IE#J{(M5X`A-?7hHRG zVk`}?3dZYw%~V}3iK9TpRQTf}V;UcloYj|o57`r}hc zk4=FEgVL!m?TJ!C=C@q)A_Z=d_r-o+B!@NJ~9X zJeK(B#}0{=^H^QdGuRPzBiL?n}Kd5C`>OmE&G^vHKObSUAqH?8DLbV%( z43kWQR#NkRst2s?PXtg*B7Bx=#R?Rc8^Qrhx!R_={sq^3?7>eyeEQL|3SiBqqAMz* zt2$C=iY%Q=D^$#28|aG0?jRzlc~LChjoW9&7?{}zm?9%Lb#!zhEUzpC%Fy^>BfMnT}!fvOdz1!2AJ!|)%iGItbZ$# z^dG{5o;D{!VOB0qCn6n^=!6v#lGrs@fpWS}NxTQ9P{#?E}&k8G{gsh&vG` zYM6h39U79YZ-)duHoXqkAiN!%w2iFrhqaEA}zNLSTruCk_VuK57&oQ4Az3WsJ=&I z0?zcC6N?M;Il)juyYTE3W3|`5;2Mu%s-&+N-0OzpAnjGCsb?y-E265@@;ta6gT+M8 z>CqKW0d>o+K;foq4t@GtuKLQ;p0!k23Fao;B3f|UZ3!0kkAz#K!^}aExd9V!*9m;2 zT#9b8sS=T2l?l1A66>}`O_Oi|u434ja3U*mD$_Q+EyAhRZ)}Pv4YRX>8JP1khcsmP zou_JT7Gw~!gT#u40y$mmM13jYR^Fz`hSSOc3a{?*RJ2>lLgmFl@ffVIGcOTKbqHe( ztW|GIN}c3;5+wwaM1!Rvf=6PR{y{0DrJAQwMq>fhQ&Gx`;F_ra)i7T5tQDu+UTlCpQQU;mI6QX^sByn(X+-g{6 z1BLkZpj#d5wjCdH=mK6QoY+4ITP2+8Mf#ISGJo3m%g>I=6Omyhnks}@a7XMwSopye zZ(e$Q!~jC4#8rx+l5pJ-zHoLQk8p^no-T8-*ZJSFl3R`9su5RBfL4j*UI;nI=l`v4 z9;A|Jol1@jRAcB+2OcUt;)QCi_~qbvc_N_|;V%wjU{u0Dif^S-z9D4Y#1X|0fQFmv zo`2299{j{3XNMST=Cd*ZUqrN_>1d^A6>c=F0!iaLY-Tt8)&K6d|H}tXEl$^?s~v7e zDF<1XMj9w9;r26^y|`muK3&fvl&W8%YHik&EzA<<8kzhigRem>$6Dvr8mldhi?oyc(Da+w%fsqIMUH zt3<+h9G_m>087=vDF;AJOx-Eiih7QzB2{~=c)ilz%cz<*Ag-X}Xlx~#zGrBxWt`<* zQ}|iao;Y&0h_Tw2_8P1JO_idZaTzaCYq8IP=^+XUITZHO4X;1=@E3|CR*J4-TqxXj z+=!v97~6lV3kgiIiq2wIj9tE|#Ms3;)GXcdsiAIJ}@EK4m53Y3TCiyuCKF(@P; zh&otOC6-z+2^dIXqE5n0CW%N8W`<-YnL+M7r}y$lpYGm|wf0`SyYHPjbu+oQPxs#4 zeNONF?QebSTLMk04}nQe5RfyJf}Dw$-ct(Hr2|1zJrI(%Td=u43-U*-BY9Bfs2D02 z)Y-C-h=#L$CDdub7Xx!_7C|Z|9IxC7z2C|I-KDZt(5FK=PG#MQ)be(2Qv%yOLFgGQ z+vf?UtgRwpI|Hi7;c}SBAXj`4WT|@zmMWBOkq(li<>dqTDu9xFN62aDwa`k}Cb5fF z%yD4aiy{(|@u^@V0W@U_FTVVd(<4yWi`qALL#VX{Ot!;gXXa^8#g1br3EDttzY3X8{ULM^kVd zGG-8! zC`Din#MmOaVXNvi!B`%^(v)`I9Qq0b)mJ8C5hdOyx*}uD#pgcjp~oI!GYVaF#a?Es zUcdD4SwDDmrCvAbGyqJ&K2e`k1hAA=SV|rU3YAid=~E$v83W*Oln-iYekD%Va`v1@ z%Df=w=2c=6mh&FdBo;s_HBWW&SuzY1DTLw|lD$^|Nl1qv6I#jDVw%k&Ox*?tT498) zTI7T-U?LYy$n zp#WN?O&+F$Rstl#44xE>8+e(Q)K1DV%iUO+>WYhZ{re;Tacad_GXXYJK!~B;Th5@!Hsc{at;d14!WAB zNSWqKycz*fyg>GaS;(B8YlxLN$)q-`?+*H6i@jLPRbICDqCGcs>KJw&RZP`FaBGoz zV$Rhm0Rmz>-!yWnl}@V>Y&ko0Y#9QyqN`QI=&bcoT)Z+f7db2d`PQW}HARkk-S=Yf zaaEi)2vO!B%Vtf0rP`A(0Z;4>Wg_}Ku94vt6;oi8gfu$wyKkUCNe>9jQFXp#N)}gz z-ti39LrUXHLJsdCP<0xJu>zZhM7&KlTws|EIkgRxa`>cA0VGQhvP)Zo7?W5Hy1H@4 zMQbY;&@LyZp{!nVc>9X70+h|_BY8%eoI_cK#ybZTvPYoPxM*Ua#*D-fFea5L;m;zdo4wSs&Y4RQ`)LK09@gnh8i|k zT)gYl1`17zJDC_=m25%XAy`KCG$fY0<>_c{JpZDOyRv93n%b;Au;bDPcKqC&mq_uP4;^DQ3*#KwPPx&W!~g^F*ORO`%Sl0q z9kipZ9@|iI<^+xe9Mh5u+JkZLvp z)ho#q)kHOsC8(j<-fe{LUFpaAzqJ2$g7>apvP4ASxHeFC=Iv$n=g*@nqVCEm`tp$Q zyL(r|K&A6TG#Fh)EeDU3R5d#)@|dcSNI`mK;t};rMv~O241-dP9VNj~!VoKAH!6km zBvh0rM_8h2PtHjQur^z}`KR9g(jVAq(3~;`E9s&xGfXL9B>*f`REf?Mcm`G&mT^Eu z#}@0nXFb~5Vj;kes(QtlPXjY+yw|_J>al@RLsNp!OOC zP;f9H$e-hztPwN8@N_?r%D}-y>4Y%Q`+Hd9L%2PilpZ!k=>-{S9cJ#EGQ$9L#lk^M z62zR5Rq`>ZZM~?M@BY$l_(uKjnv{pMeL#;%=54n9<`FM*X|VM@dax#G~VIhm$# zp)^QPL};~JTqDuI0t*8)<2q>(2M0p&R)jJbHC52KlQ2iDyOr+%CEB$^&Rle$5koQ} zm8qN~lVrLzYmiKsTT5y@;FWqL@Ozf+F5l2^#cEoD(y(nBN0diJhI z4xbtx%e86HhTd}Y6^ODP#1st%LVzbhZ)E~@4B|0po^$TAAA0PO+Ae>nn!W9UXPRp5 zz>b$3+5Q7Zd+&7wHSS5wtdR6afL!hBO!m=a01Na|dWE>SJb|kWU_q%SeS~Gh@W!*y zFyC^nHgnbpl%4)zi0mCSsf(cXz6UOlDu30}cD3B|&_JtqeeuJ4AN=AszIV7Ld&agi zcE5J_e7<(U&gUID_z(ivx<GPP#$mfpbG%fUJUuPK^P*yq7u9XNhUcD$V4T+5=?1x4i$NYR1j#;AR$G|TD#HA zT^`aJg^z^LNF%%K1@G%yEqI?5g{2a^<KkYm9a8G#0EOO?>UEf^hCkM>T+QNIU*bXWi0oN;aI zrM2(*(npSOocPZAG26%XXt`X@XY<*7ZP)eJT3g?H>t4IQuD z&~7)iLFDQpR~7)fJyBw@I{vAyy1tAxkXL<$-CAQ2u(``KL>sAov2EEJ3rXK80H~Zj z(+rle2HQQ2H8Gio0M<}Q!cx@eLN3S{r%X{4(MGRk)t;CcW_XA_N))%tSdz56~IK&x(ez4FCZ z-uC|6X7gEdn6w9nAOY4ieg;}6g!As>Ade~G3t!M(ECVDhbe*d%|1d(b_rs6dvfmg~=EZqgk zz?bA|+=+BZlBN30nGhvfBj5nN5Lx!lY-73c2VeM;d%wMZD>2qA@t&>hKfL(f?_r(T z=M0+t?EcFL;O}?-`$_Dx^UkE%&+gv^0H51=4**=Ve;2k`!9`blFS#24uDSnZ0I>Ix zyH+g-6xen{x{1ffHBE~}mr)0CRu#1UEtmJ{& zf;TbwB(p&P87qd-R1-X&Y$iXKcwcJ!&+xqC^M_0OP1RyY`GF7$fhHI^@@BKsvvm#<_ zRR{-!qCK!8BB5od+U;FCZ9SgmCiOSknOa;s3-7V(X)3p|ReNnCEko8=Lb#6Dek8Zd zSzMk+bEqqWsM7lGA*Ku<$d`umTRHNK#zX*lFjZjgSxi}#vR31WEB0;VZyMhqiZ_@6 zeJMu?fAiXG?dGe0`S-u@C--e3#+oJQD+Kr+){mRzIW&{1g$5z0$8iY&Pp4UTZ6|Sw z@=WbwvXi)c%q-8P85>i043}LVzzQK@u`?C|oHWa$xHymI?6F62(Ir-%Fw3L3IA7S) zeVs6iBe>W>^K{qEp3=JnaH2(i@YtN=^Bhf za4HN}7AQ;}FE+);*%apxJ8MN_LjawWo?Z~)!BqS1`E#*IkZi*|z~NLT#IlCS!#O@E ziNv6^wb}gUt8aSO-+l1mH1X>^nqPn5s;V|YB*s?uFQ0#h zyXW~d-+l0vRc+#65VXO>ChE6?=D&3CRh6wI$$z)!R4-%An;-shp97;myZE00;HHOv zvWZB7A0jM-L?zTwo}(hv)|H-+#2QSJu>dYll5qr&3Lp}HfATJ%oY;%I+Q{IPEzd06 zCYCCTa1wP@k2ytG$fg1BO4`5$^rjYM%4icIY%CY=-M6QS31(HkhrV@a_iyaJ?R~e+ zW;1Uv#ub}&*)Lve>-xTr?_E6cy|!QQ6k0vZ;!ercuJt~=)OO->SFKf}H=qy&gq8wn zfdjTMV78nkDEXt7a{5&S4Z*eW)a}n=sf;`3r>*W!ks*SPjPy2gSGXckL&&oQ7w12ug z2Rj5mMQ5(`xN1(L`)NQGdfTz$dM^(AWhxb@AIqU^x=J`fW6os4_BH-hsj-@;5HeFC z1y?|P`;;0f`(^}dD9Xn*gi(@p;UPi)5gHoBr&QZ|eX;T0eIGsWp{WbHm&+tFC$d{~jdjj8J6IONxj!zc_m*Cfvq0SF0Fhrof z{2%p>Pu8nf)3{Rghqtu|9eb+A&Waj)tyNczfmZzEI!XmwZ0BCZyn?$3Lctu*D$DhP z2x>=D)�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 Date: Tue, 31 Oct 2023 20:15:45 +0100 Subject: [PATCH 0146/1037] REDESIGNED: `LoadOBJ()`, fix #3398 - Now triangulated meshes are properly supported - Simplified code to consider issue situation - Removed mesh split per material, just define separate mesh if multiple materials are required --- src/rmodels.c | 155 +++++++++++++++++++++----------------------------- 1 file changed, 65 insertions(+), 90 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 68c2d75bc..0fc305ddc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3931,9 +3931,10 @@ static Model LoadOBJ(const char *fileName) if (fileText != NULL) { unsigned int dataSize = (unsigned int)strlen(fileText); + char currentDir[1024] = { 0 }; - strcpy(currentDir, GetWorkingDirectory()); - const char *workingDir = GetDirectoryPath(fileName); + strcpy(currentDir, GetWorkingDirectory()); // Save current working directory + const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness if (CHDIR(workingDir) != 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir); @@ -3945,117 +3946,91 @@ static Model LoadOBJ(const char *fileName) if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName); else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes/%i materials", fileName, meshCount, materialCount); - model.meshCount = materialCount; + // WARNING: We are not splitting meshes by materials (previous implementation) + // Depending on the provided OBJ that was not the best option and it just crashed + // so, implementation was simplified to prioritize parsed meshes + model.meshCount = meshCount; - // Init model materials array - if (materialCount > 0) + // Set number of materials available + // NOTE: There could be more materials available than meshes but it will be resolved at + // model.meshMaterial, just assigning the right material to corresponding mesh + model.materialCount = materialCount; + if (model.materialCount == 0) { - model.materialCount = materialCount; - model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); - TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", materialCount); - } - else - { - model.meshCount = 1; - TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); + model.materialCount = 1; + TRACELOG(LOG_INFO, "MODEL: No materials provided, setting one default material for all meshes"); } + // Init model meshes and materials model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); - model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); // Material index assigned to each mesh + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); - // Count the faces for each material - int *matFaces = RL_CALLOC(model.meshCount, sizeof(int)); - - // if no materials are present use all faces on one mesh - if (materialCount > 0) + // Process each provided mesh + for (int i = 0; i < model.meshCount; i++) { - for (unsigned int fi = 0; fi < attrib.num_faces; fi++) + // WARNING: We need to calculate the mesh triangles manually using meshes[i].face_offset + // because in case of triangulated quads, meshes[i].length actually report quads, + // despite the triangulation that is efectively considered on attrib.num_faces + unsigned int tris = 0; + if (i == model.meshCount - 1) tris = attrib.num_faces - meshes[i].face_offset; + else tris = meshes[i + 1].face_offset; + + model.meshes[i].vertexCount = tris*3; + model.meshes[i].triangleCount = tris; // Face count (triangulated) + model.meshes[i].vertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshes[i].texcoords = (float *)RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); + model.meshes[i].normals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshMaterial[i] = 0; // By default, assign material 0 to each mesh + + // Process all mesh faces + for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 2, vn += 3) { - //tinyobj_vertex_index_t face = attrib.faces[fi]; - int idx = attrib.material_ids[fi]; - matFaces[idx]++; - } + // Get indices for the face + tinyobj_vertex_index_t idx0 = attrib.faces[f*3 + 0]; + tinyobj_vertex_index_t idx1 = attrib.faces[f*3 + 1]; + tinyobj_vertex_index_t idx2 = attrib.faces[f*3 + 2]; - } - else - { - matFaces[0] = attrib.num_faces; - } + // Fill vertices buffer (float) using vertex index of the face + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[v*3 + n] = attrib.vertices[idx0.v_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 1)*3 + n] = attrib.vertices[idx1.v_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 2)*3 + n] = attrib.vertices[idx2.v_idx*3 + n]; } - //-------------------------------------- - // Create the material meshes + if (attrib.num_texcoords > 0) + { + // Fill texcoords buffer (float) using vertex index of the face + // NOTE: Y-coordinate must be flipped upside-down + model.meshes[i].texcoords[vt*2 + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; + model.meshes[i].texcoords[vt*2 + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; - // Running counts/indexes for each material mesh as we are - // building them at the same time - int *vCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *vtCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *vnCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *faceCount = RL_CALLOC(model.meshCount, sizeof(int)); + model.meshes[i].texcoords[(vt + 1)*2 + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; + model.meshes[i].texcoords[(vt + 1)*2 + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; - // Allocate space for each of the material meshes - for (int mi = 0; mi < model.meshCount; mi++) - { - model.meshes[mi].vertexCount = matFaces[mi]*3; - model.meshes[mi].triangleCount = matFaces[mi]; - model.meshes[mi].vertices = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); - model.meshes[mi].texcoords = (float *)RL_CALLOC(model.meshes[mi].vertexCount*2, sizeof(float)); - model.meshes[mi].normals = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); - model.meshMaterial[mi] = mi; - } + model.meshes[i].texcoords[(vt + 2)*2 + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; + model.meshes[i].texcoords[(vt + 2)*2 + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; + } - // Scan through the combined sub meshes and pick out each material mesh - for (unsigned int af = 0; af < attrib.num_faces; af++) - { - int mm = attrib.material_ids[af]; // mesh material for this face - if (mm == -1) { mm = 0; } // no material object.. - - // Get indices for the face - tinyobj_vertex_index_t idx0 = attrib.faces[3*af + 0]; - tinyobj_vertex_index_t idx1 = attrib.faces[3*af + 1]; - tinyobj_vertex_index_t idx2 = attrib.faces[3*af + 2]; - - // Fill vertices buffer (float) using vertex index of the face - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount[mm] +=3; - - if (attrib.num_texcoords > 0) - { - // Fill texcoords buffer (float) using vertex index of the face - // NOTE: Y-coordinate must be flipped upside-down to account for - // raylib's upside down textures... - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount[mm] += 2; - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount[mm] += 2; - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount[mm] += 2; - } - - if (attrib.num_normals > 0) - { - // Fill normals buffer (float) using vertex index of the face - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount[mm] +=3; + if (attrib.num_normals > 0) + { + // Fill normals buffer (float) using vertex index of the face + for (int n = 0; n < 3; n++) { model.meshes[i].normals[vn*3 + n] = attrib.normals[idx0.vn_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 1)*3 + n] = attrib.normals[idx1.vn_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 2)*3 + n] = attrib.normals[idx2.vn_idx*3 + n]; } + } } } // Init model materials - ProcessMaterialsOBJ(model.materials, materials, materialCount); + if (materialCount > 0) ProcessMaterialsOBJ(model.materials, materials, materialCount); + else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh tinyobj_attrib_free(&attrib); - tinyobj_shapes_free(meshes, meshCount); + tinyobj_shapes_free(meshes, model.meshCount); tinyobj_materials_free(materials, materialCount); UnloadFileText(fileText); - RL_FREE(matFaces); - RL_FREE(vCount); - RL_FREE(vtCount); - RL_FREE(vnCount); - RL_FREE(faceCount); - + // Restore current working directory if (CHDIR(currentDir) != 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir); From 15142a30f5e371b4a8246d9a8671ae260bb4c78f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:20:11 +0100 Subject: [PATCH 0147/1037] Update rmodels.c --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 0fc305ddc..3cae43d12 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3984,7 +3984,7 @@ static Model LoadOBJ(const char *fileName) model.meshMaterial[i] = 0; // By default, assign material 0 to each mesh // Process all mesh faces - for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 2, vn += 3) + for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 3, vn += 3) { // Get indices for the face tinyobj_vertex_index_t idx0 = attrib.faces[f*3 + 0]; From 1407f6eb4673dbf480ce9c7c33cfe54703bad599 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:46:06 +0100 Subject: [PATCH 0148/1037] ADDED: `rlBlitFramebuffer()`, required for deferred render --- src/rlgl.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index fcb8feeb6..da6573d62 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -617,6 +617,7 @@ RLAPI void rlDisableShader(void); // Disable shader progra RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo) RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer RLAPI void rlActiveDrawBuffers(int count); // Activate multiple draw color buffers +RLAPI void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask); // Blit active framebuffer to main framebuffer // General render state RLAPI void rlEnableColorBlend(void); // Enable color blending @@ -1713,6 +1714,14 @@ void rlDisableFramebuffer(void) #endif } +// Blit active framebuffer to main framebuffer +void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBlitFramebuffer(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask, GL_NEAREST); +#endif +} + // Activate multiple draw color buffers // NOTE: One color buffer is always active by default void rlActiveDrawBuffers(int count) From aca854ccbf1fe9fac04f2067ffc90960e621e927 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:46:10 +0100 Subject: [PATCH 0149/1037] Update shaders_deferred_render.c --- examples/shaders/shaders_deferred_render.c | 117 ++++++++++++--------- 1 file changed, 65 insertions(+), 52 deletions(-) diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c index 58d4b1dd9..5bb4db1d2 100644 --- a/examples/shaders/shaders_deferred_render.c +++ b/examples/shaders/shaders_deferred_render.c @@ -2,7 +2,7 @@ * * raylib [shaders] example - deferred rendering * -* NOTE: This example requires raylib OpenGL 3.3 or ES 3 versions. +* NOTE: This example requires raylib OpenGL 3.3 or OpenGL ES 3.0 * * Example originally created with raylib 4.5, last time updated with raylib 4.5 * @@ -15,12 +15,9 @@ * ********************************************************************************************/ -#include -#include - #include "raylib.h" -#include "rlgl.h" +#include "rlgl.h" #include "raymath.h" #define RLIGHTS_IMPLEMENTATION @@ -32,7 +29,9 @@ #define GLSL_VERSION 100 #endif -typedef struct { +#include // Required for: NULL + +typedef struct GBuffer { unsigned int framebuffer; unsigned int positionTexture; @@ -42,7 +41,18 @@ typedef struct { unsigned int depthRenderbuffer; } GBuffer; -int main(void) { +typedef enum { + DEFERRED_POSITION, + DEFERRED_NORMAL, + DEFERRED_ALBEDO, + DEFERRED_SHADING +} DeferredMode; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ // Initialization // ------------------------------------------------------------------------------------- const int screenWidth = 800; @@ -73,11 +83,12 @@ int main(void) { GBuffer gBuffer = { 0 }; gBuffer.framebuffer = rlLoadFramebuffer(screenWidth, screenHeight); - if(!gBuffer.framebuffer) + if (!gBuffer.framebuffer) { TraceLog(LOG_WARNING, "Failed to create framebuffer"); exit(1); } + rlEnableFramebuffer(gBuffer.framebuffer); // Since we are storing position and normal data in these textures, @@ -104,7 +115,7 @@ int main(void) { // Make sure our framebuffer is complete. // NOTE: rlFramebufferComplete() automatically unbinds the framebuffer, so we don't have // to rlDisableFramebuffer() here. - if(rlFramebufferComplete(gBuffer.framebuffer) != true) + if (!rlFramebufferComplete(gBuffer.framebuffer)) { TraceLog(LOG_WARNING, "Framebuffer is not complete"); exit(1); @@ -137,22 +148,18 @@ int main(void) { const float CUBE_SCALE = 0.25; Vector3 cubePositions[MAX_CUBES]; float cubeRotations[MAX_CUBES]; + for(int i = 0; i < MAX_CUBES; i++) { - cubePositions[i] = (Vector3) { - .x = (float)(rand() % 10) - 5, - .y = (float)(rand() % 5), - .z = (float)(rand() % 10) - 5, + cubePositions[i] = (Vector3){ + .x = (float)(rand()%10) - 5, + .y = (float)(rand()%5), + .z = (float)(rand()%10) - 5, }; - cubeRotations[i] = (float)(rand() % 360); + cubeRotations[i] = (float)(rand()%360); } - enum { - POSITION, - NORMAL, - ALBEDO, - DEFERRED_SHADING - } activeTexture = DEFERRED_SHADING; + DeferredMode mode = DEFERRED_SHADING; rlEnableDepthTest(); @@ -177,12 +184,11 @@ int main(void) { if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; } // Check key inputs to switch between G-buffer textures - if(IsKeyPressed(KEY_ONE)) activeTexture = POSITION; - if(IsKeyPressed(KEY_TWO)) activeTexture = NORMAL; - if(IsKeyPressed(KEY_THREE)) activeTexture = ALBEDO; - if(IsKeyPressed(KEY_FOUR)) activeTexture = DEFERRED_SHADING; + if (IsKeyPressed(KEY_ONE)) mode = DEFERRED_POSITION; + if (IsKeyPressed(KEY_TWO)) mode = DEFERRED_NORMAL; + if (IsKeyPressed(KEY_THREE)) mode = DEFERRED_ALBEDO; + if (IsKeyPressed(KEY_FOUR)) mode = DEFERRED_SHADING; - // Update light values (actually, only enable/disable them) for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(deferredShader, lights[i]); //---------------------------------------------------------------------------------- @@ -190,14 +196,16 @@ int main(void) { // Draw // --------------------------------------------------------------------------------- BeginDrawing(); - // Draw to the geometry buffer by first activating it. + + ClearBackground(RAYWHITE); + + // Draw to the geometry buffer by first activating it rlEnableFramebuffer(gBuffer.framebuffer); - rlClearScreenBuffers(); // Clear color & depth buffer - + rlClearScreenBuffers(); // Clear color and depth buffer + rlDisableColorBlend(); BeginMode3D(camera); - // NOTE: - // We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` + // NOTE: We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` // will not work, as they won't immediately load the shader program. rlEnableShader(gbufferShader.id); // When drawing a model here, make sure that the material's shaders @@ -205,10 +213,9 @@ int main(void) { DrawModel(model, Vector3Zero(), 1.0f, WHITE); DrawModel(cube, (Vector3) { 0.0, 1.0f, 0.0 }, 1.0f, WHITE); - for(int i = 0; i < MAX_CUBES; i++) + for (int i = 0; i < MAX_CUBES; i++) { Vector3 position = cubePositions[i]; - DrawModelEx(cube, position, (Vector3) { 1, 1, 1 }, cubeRotations[i], (Vector3) { CUBE_SCALE, CUBE_SCALE, CUBE_SCALE }, WHITE); } @@ -220,9 +227,10 @@ int main(void) { rlDisableFramebuffer(); rlClearScreenBuffers(); // Clear color & depth buffer - switch(activeTexture) + switch (mode) { case DEFERRED_SHADING: + { BeginMode3D(camera); rlDisableColorBlend(); rlEnableShader(deferredShader.id); @@ -243,11 +251,10 @@ int main(void) { rlEnableColorBlend(); EndMode3D(); - // As a last step, we now copy over the depth buffer from our g-buffer to the - // default framebuffer. - glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer.framebuffer); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + // As a last step, we now copy over the depth buffer from our g-buffer to the default framebuffer. + rlEnableFramebuffer(gBuffer.framebuffer); //glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer.framebuffer); + rlEnableFramebuffer(0); //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + rlBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, 0x00000100); // GL_DEPTH_BUFFER_BIT rlDisableFramebuffer(); // Since our shader is now done and disabled, we can draw our lights in default @@ -256,44 +263,50 @@ int main(void) { rlEnableShader(rlGetShaderIdDefault()); for(int i = 0; i < MAX_LIGHTS; i++) { - if(lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color); + if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color); else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f)); } rlDisableShader(); EndMode3D(); DrawText("FINAL RESULT", 10, screenHeight - 30, 20, DARKGREEN); - break; - case POSITION: - DrawTextureRec((Texture2D) { + } break; + + case DEFERRED_POSITION: + { + DrawTextureRec((Texture2D){ .id = gBuffer.positionTexture, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); - break; - case NORMAL: - DrawTextureRec((Texture2D) { + } break; + + case DEFERRED_NORMAL: + { + DrawTextureRec((Texture2D){ .id = gBuffer.normalTexture, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); - break; + } break; - case ALBEDO: - DrawTextureRec((Texture2D) { + case DEFERRED_ALBEDO: + { + DrawTextureRec((Texture2D){ .id = gBuffer.albedoSpecTexture, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); - break; + } break; } - DrawFPS(10, 10); + DrawText("Toggle lights keys: [Y][R][G][B]", 10, 40, 20, DARKGRAY); + DrawText("Switch G-buffer textures: [1][2][3][4]", 10, 70, 20, DARKGRAY); - DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, DARKGRAY); - DrawText("Use keys [1]-[4] to switch between G-buffer textures", 10, 70, 20, DARKGRAY); + DrawFPS(10, 10); + EndDrawing(); // ----------------------------------------------------------------------------- } From df0f7ba61e6a89e493d8b6313001ecf6a7231e5a Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:59:08 +0100 Subject: [PATCH 0150/1037] Update shaders_deferred_render.c --- examples/shaders/shaders_deferred_render.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c index 5bb4db1d2..21d2af346 100644 --- a/examples/shaders/shaders_deferred_render.c +++ b/examples/shaders/shaders_deferred_render.c @@ -31,6 +31,8 @@ #include // Required for: NULL +#define MAX_CUBES 30 + typedef struct GBuffer { unsigned int framebuffer; @@ -144,18 +146,18 @@ int main(void) lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, deferredShader); lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, deferredShader); - const int MAX_CUBES = 30; const float CUBE_SCALE = 0.25; - Vector3 cubePositions[MAX_CUBES]; - float cubeRotations[MAX_CUBES]; + Vector3 cubePositions[MAX_CUBES] = { 0 }; + float cubeRotations[MAX_CUBES] = { 0 }; - for(int i = 0; i < MAX_CUBES; i++) + for (int i = 0; i < MAX_CUBES; i++) { cubePositions[i] = (Vector3){ .x = (float)(rand()%10) - 5, .y = (float)(rand()%5), .z = (float)(rand()%10) - 5, }; + cubeRotations[i] = (float)(rand()%360); } From d4c0d3bebe9e1f7bdaa197b77fc868aca683ad35 Mon Sep 17 00:00:00 2001 From: Techuuu <135726634+Techuuu@users.noreply.github.com> Date: Wed, 1 Nov 2023 16:45:47 +0530 Subject: [PATCH 0151/1037] Update README.md (#3495) * Update README.md Fixed docs and improved. * Update README.md Done fixed it! * Update README.md Fixed! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bff6980b0..fadc3b659 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ features basic example -------------- -This is a basic raylib example, it creates a window and it draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window). +This is a basic raylib example, it creates a window and draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window). ```c #include "raylib.h" From 38205d67da4cf1216bbc4824e4c3af25e51792e0 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 1 Nov 2023 04:16:14 -0700 Subject: [PATCH 0152/1037] Remove unused structures from lighting fragment shaders (#3497) --- examples/shaders/resources/shaders/glsl100/lighting.fs | 6 ------ examples/shaders/resources/shaders/glsl120/lighting.fs | 6 ------ examples/shaders/resources/shaders/glsl330/lighting.fs | 6 ------ 3 files changed, 18 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/lighting.fs b/examples/shaders/resources/shaders/glsl100/lighting.fs index 736716198..73531a8bb 100644 --- a/examples/shaders/resources/shaders/glsl100/lighting.fs +++ b/examples/shaders/resources/shaders/glsl100/lighting.fs @@ -18,12 +18,6 @@ uniform vec4 colDiffuse; #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 -struct MaterialProperty { - vec3 color; - int useSampler; - sampler2D sampler; -}; - struct Light { int enabled; int type; diff --git a/examples/shaders/resources/shaders/glsl120/lighting.fs b/examples/shaders/resources/shaders/glsl120/lighting.fs index d9cfb4472..8dda5a549 100644 --- a/examples/shaders/resources/shaders/glsl120/lighting.fs +++ b/examples/shaders/resources/shaders/glsl120/lighting.fs @@ -16,12 +16,6 @@ uniform vec4 colDiffuse; #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 -struct MaterialProperty { - vec3 color; - int useSampler; - sampler2D sampler; -}; - struct Light { int enabled; int type; diff --git a/examples/shaders/resources/shaders/glsl330/lighting.fs b/examples/shaders/resources/shaders/glsl330/lighting.fs index 58845c86b..d1f3140a1 100644 --- a/examples/shaders/resources/shaders/glsl330/lighting.fs +++ b/examples/shaders/resources/shaders/glsl330/lighting.fs @@ -19,12 +19,6 @@ out vec4 finalColor; #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 -struct MaterialProperty { - vec3 color; - int useSampler; - sampler2D sampler; -}; - struct Light { int enabled; int type; From ba21b8d2744b39dadc6b17b9091a30cc5e00f0b8 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 1 Nov 2023 08:20:33 -0300 Subject: [PATCH 0153/1037] Moves keymapUS[] and fixes GetCharPressed for DRM (#3498) --- src/platforms/rcore_drm.c | 66 ++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 632686767..c6307773f 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -137,6 +137,39 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data +//---------------------------------------------------------------------------------- +// Local Variables Definition +//---------------------------------------------------------------------------------- +// Scancode to keycode mapping for US keyboards +// TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard: +// Currently non US keyboards will have the wrong mapping for some keys +static const int keymapUS[] = { + 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, + 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96, + 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325, + 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95, + 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 +}; + +// NOTE: The complete evdev EV_KEY list can be found at /usr/include/linux/input-event-codes.h +// TODO: Complete the LUT with all unicode decimal values +static const int EvkeyToUnicodeLUT[] = { + 0, 27, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 8, 0, 113, 119, 101, 114, + 116, 121, 117, 105, 111, 112, 0, 0, 13, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 59, + 39, 96, 0, 92, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 32 + // LUT currently incomplete, just mapped the most essential keys +}; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -1454,27 +1487,6 @@ static void ConfigureEvdevDevice(char *device) // Poll and process evdev keyboard events static void PollKeyboardEvents(void) { - // Scancode to keycode mapping for US keyboards - // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard: - // Currently non US keyboards will have the wrong mapping for some keys - static const int keymapUS[] = { - 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, - 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96, - 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291, - 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325, - 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95, - 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, - 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, - 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, - 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 - }; - int fd = platform.keyboardFd; if (fd == -1) return; @@ -1518,6 +1530,18 @@ static void PollKeyboardEvents(void) } #endif + // Detect char presses (unicode) + if (event.value == 1) + { + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = EvkeyToUnicodeLUT[event.code]; + CORE.Input.Keyboard.charPressedQueueCount++; + } + } + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", (event.value == 0)? "UP" : "DOWN", event.code, keycode); From 64d64cc18114c02ecb068b20b6c4820df6182415 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Nov 2023 15:28:18 +0100 Subject: [PATCH 0154/1037] REVIEWED: Potential code issues reported by CodeQL #3476 --- examples/core/core_automation_events.c | 6 +-- examples/core/core_loading_thread.c | 2 +- examples/core/core_random_values.c | 2 +- examples/models/models_skybox.c | 13 ++--- examples/others/raymath_vector_angle.c | 2 +- examples/shaders/shaders_raymarching.c | 3 +- examples/text/text_unicode.c | 18 +++---- src/rcore.c | 6 +-- src/rlgl.h | 63 ++++++++++++------------ src/rmodels.c | 4 +- src/rtext.c | 67 ++++++++++++++------------ src/rtextures.c | 4 +- 12 files changed, 98 insertions(+), 92 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index 27711b39d..0739a6e7d 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -75,9 +75,9 @@ int main(void) bool eventRecording = false; bool eventPlaying = false; - int frameCounter = 0; - int playFrameCounter = 0; - int currentPlayFrame = 0; + unsigned int frameCounter = 0; + unsigned int playFrameCounter = 0; + unsigned int currentPlayFrame = 0; SetTargetFPS(60); //-------------------------------------------------------------------------------------- diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 0538dcee3..8451ff037 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -41,7 +41,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [core] example - loading thread"); - pthread_t threadId; // Loading data thread id + pthread_t threadId = { 0 }; // Loading data thread id enum { STATE_WAITING, STATE_LOADING, STATE_FINISHED } state = STATE_WAITING; int framesCounter = 0; diff --git a/examples/core/core_random_values.c b/examples/core/core_random_values.c index c2225bcaf..bec1de27b 100644 --- a/examples/core/core_random_values.c +++ b/examples/core/core_random_values.c @@ -29,7 +29,7 @@ int main(void) int randValue = GetRandomValue(-8, 5); // Get a random integer number between -8 and 5 (both included) - int framesCounter = 0; // Variable used to count frames + unsigned int framesCounter = 0; // Variable used to count frames SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index 7a500e04d..c583128b6 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -68,14 +68,12 @@ int main(void) char skyboxFileName[256] = { 0 }; - Texture2D panorama; - if (useHDR) { TextCopy(skyboxFileName, "resources/dresden_square_2k.hdr"); // Load HDR panorama (sphere) texture - panorama = LoadTexture(skyboxFileName); + Texture2D panorama = LoadTexture(skyboxFileName); // Generate cubemap (texture with 6 quads-cube-mapping) from panorama HDR texture // NOTE 1: New texture is generated rendering to texture, shader calculates the sphere->cube coordinates mapping @@ -83,7 +81,7 @@ int main(void) // despite texture can be successfully created.. so using PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 instead of PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - //UnloadTexture(panorama); // Texture not required anymore, cubemap already generated + UnloadTexture(panorama); // Texture not required anymore, cubemap already generated } else { @@ -113,15 +111,18 @@ int main(void) { if (IsFileExtension(droppedFiles.paths[0], ".png;.jpg;.hdr;.bmp;.tga")) { - // Unload current cubemap texture and load new one + // Unload current cubemap texture to load new one UnloadTexture(skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture); + if (useHDR) { + // Load HDR panorama (sphere) texture Texture2D panorama = LoadTexture(droppedFiles.paths[0]); // Generate cubemap from panorama texture skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - UnloadTexture(panorama); + + UnloadTexture(panorama); // Texture not required anymore, cubemap already generated } else { diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index ad573f54d..d0f81548c 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -42,7 +42,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - float startangle; + float startangle = 0.0f; if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; if (angleMode == 1) startangle = 0.0f; diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index e9b7755ad..ff403e619 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -82,7 +82,8 @@ int main(void) // Check if screen is resized if (IsWindowResized()) { - float resolution[2] = { (float)GetScreenWidth(), (float)GetScreenHeight() }; + resolution[0] = (float)GetScreenWidth(); + resolution[1] = (float)GetScreenHeight(); SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2); } //---------------------------------------------------------------------------------- diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index b25e3273b..42227f89c 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -195,7 +195,7 @@ int main(void) } Vector2 mouse = GetMousePosition(); - Vector2 pos = { 28.8f, 10.0f }; + Vector2 position = { 28.8f, 10.0f }; hovered = -1; //---------------------------------------------------------------------------------- @@ -210,21 +210,21 @@ int main(void) for (int i = 0; i < SIZEOF(emoji); ++i) { const char *txt = &emojiCodepoints[emoji[i].index]; - Rectangle emojiRect = { pos.x, pos.y, (float)fontEmoji.baseSize, (float)fontEmoji.baseSize }; + Rectangle emojiRect = { position.x, position.y, (float)fontEmoji.baseSize, (float)fontEmoji.baseSize }; if (!CheckCollisionPointRec(mouse, emojiRect)) { - DrawTextEx(fontEmoji, txt, pos, (float)fontEmoji.baseSize, 1.0f, selected == i ? emoji[i].color : Fade(LIGHTGRAY, 0.4f)); + DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, selected == i ? emoji[i].color : Fade(LIGHTGRAY, 0.4f)); } else { - DrawTextEx(fontEmoji, txt, pos, (float)fontEmoji.baseSize, 1.0f, emoji[i].color ); + DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, emoji[i].color ); hovered = i; - hoveredPos = pos; + hoveredPos = position; } - if ((i != 0) && (i%EMOJI_PER_WIDTH == 0)) { pos.y += fontEmoji.baseSize + 24.25f; pos.x = 28.8f; } - else pos.x += fontEmoji.baseSize + 28.8f; + if ((i != 0) && (i%EMOJI_PER_WIDTH == 0)) { position.y += fontEmoji.baseSize + 24.25f; position.x = 28.8f; } + else position.x += fontEmoji.baseSize + 28.8f; } //------------------------------------------------------------------------------ @@ -282,8 +282,8 @@ int main(void) int length = GetCodepointCount(messages[message].text); const char *info = TextFormat("%s %u characters %i bytes", messages[message].language, length, size); sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f); - Vector2 pos = { textRect.x + textRect.width - sz.x, msgRect.y + msgRect.height - sz.y - 2 }; - DrawText(info, (int)pos.x, (int)pos.y, 10, RAYWHITE); + + DrawText(info, (int)(textRect.x + textRect.width - sz.x), (int)(msgRect.y + msgRect.height - sz.y - 2), 10, RAYWHITE); } //------------------------------------------------------------------------------ diff --git a/src/rcore.c b/src/rcore.c index 175d68611..d4e2573b0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -436,8 +436,8 @@ struct AutomationEvent { }; */ -static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer -static bool automationEventRecording = false; // Recording automation events flag +static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer +static bool automationEventRecording = false; // Recording automation events flag //static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing #endif //----------------------------------------------------------------------------------- @@ -2465,7 +2465,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) byteCount += sprintf(txtData + byteCount, "c %i\n", list.count); for (int i = 0; i < list.count; i++) { - byteCount += sprintf(txtData + byteCount, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type, + byteCount += snprintf(txtData + byteCount, 256, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type, list.events[i].params[0], list.events[i].params[1], list.events[i].params[2], list.events[i].params[3], autoEventTypeName[list.events[i].type]); } diff --git a/src/rlgl.h b/src/rlgl.h index da6573d62..0c695c20c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -516,28 +516,28 @@ typedef enum { // Framebuffer attachment type // NOTE: By default up to 8 color channels defined, but it can be more typedef enum { - RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 - RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1 - RL_ATTACHMENT_COLOR_CHANNEL2, // Framebuffer attachment type: color 2 - RL_ATTACHMENT_COLOR_CHANNEL3, // Framebuffer attachment type: color 3 - RL_ATTACHMENT_COLOR_CHANNEL4, // Framebuffer attachment type: color 4 - RL_ATTACHMENT_COLOR_CHANNEL5, // Framebuffer attachment type: color 5 - RL_ATTACHMENT_COLOR_CHANNEL6, // Framebuffer attachment type: color 6 - RL_ATTACHMENT_COLOR_CHANNEL7, // Framebuffer attachment type: color 7 - RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth - RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil + RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 + RL_ATTACHMENT_COLOR_CHANNEL1 = 1, // Framebuffer attachment type: color 1 + RL_ATTACHMENT_COLOR_CHANNEL2 = 2, // Framebuffer attachment type: color 2 + RL_ATTACHMENT_COLOR_CHANNEL3 = 3, // Framebuffer attachment type: color 3 + RL_ATTACHMENT_COLOR_CHANNEL4 = 4, // Framebuffer attachment type: color 4 + RL_ATTACHMENT_COLOR_CHANNEL5 = 5, // Framebuffer attachment type: color 5 + RL_ATTACHMENT_COLOR_CHANNEL6 = 6, // Framebuffer attachment type: color 6 + RL_ATTACHMENT_COLOR_CHANNEL7 = 7, // Framebuffer attachment type: color 7 + RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth + RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil } rlFramebufferAttachType; // Framebuffer texture attachment type typedef enum { - RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_X, // Framebuffer texture attachment type: cubemap, -X side - RL_ATTACHMENT_CUBEMAP_POSITIVE_Y, // Framebuffer texture attachment type: cubemap, +Y side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y, // Framebuffer texture attachment type: cubemap, -Y side - RL_ATTACHMENT_CUBEMAP_POSITIVE_Z, // Framebuffer texture attachment type: cubemap, +Z side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z, // Framebuffer texture attachment type: cubemap, -Z side - RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d - RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer + RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_X = 1, // Framebuffer texture attachment type: cubemap, -X side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Y = 2, // Framebuffer texture attachment type: cubemap, +Y side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y = 3, // Framebuffer texture attachment type: cubemap, -Y side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Z = 4, // Framebuffer texture attachment type: cubemap, +Z side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z = 5, // Framebuffer texture attachment type: cubemap, -Z side + RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d + RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer } rlFramebufferAttachTextureType; // Face culling mode @@ -823,6 +823,9 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); #endif #endif +#if defined(GRAPHICS_API_OPENGL_ES3) + #include +#endif #include // Required for: malloc(), free() #include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] @@ -2243,7 +2246,7 @@ void rlLoadExtensions(void *loader) #if defined(GRAPHICS_API_OPENGL_ES3) // Register supported extensions flags - // OpenGL ES 3.0 extensions supported by default + // OpenGL ES 3.0 extensions supported by default (or it should be) RLGL.ExtSupported.vao = true; RLGL.ExtSupported.instancing = true; RLGL.ExtSupported.texNPOT = true; @@ -2254,20 +2257,20 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.maxDepthBits = 24; RLGL.ExtSupported.texAnisoFilter = true; RLGL.ExtSupported.texMirrorClamp = true; - // TODO: Make sure that the ones above are actually present by default - // TODO: Check for these... - // RLGL.ExtSupported.texCompDXT - // RLGL.ExtSupported.texCompETC1 - // RLGL.ExtSupported.texCompETC2 - // RLGL.ExtSupported.texCompPVRT - // RLGL.ExtSupported.texCompASTC - // RLGL.ExtSupported.computeShader - // RLGL.ExtSupported.ssbo - // RLGL.ExtSupported.maxAnisotropyLevel + // TODO: Check for additional OpenGL ES 3.0 supported extensions: + //RLGL.ExtSupported.texCompDXT = true; + //RLGL.ExtSupported.texCompETC1 = true; + //RLGL.ExtSupported.texCompETC2 = true; + //RLGL.ExtSupported.texCompPVRT = true; + //RLGL.ExtSupported.texCompASTC = true; + //RLGL.ExtSupported.maxAnisotropyLevel = true; + //RLGL.ExtSupported.computeShader = true; + //RLGL.ExtSupported.ssbo = true; + #elif defined(GRAPHICS_API_OPENGL_ES2) #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) - // TODO: Support OpenGL ES 3.0 + // TODO: Support GLAD loader for OpenGL ES 3.0 if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); #endif diff --git a/src/rmodels.c b/src/rmodels.c index 3cae43d12..229d373fc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2142,11 +2142,11 @@ Mesh GenMeshPoly(int sides, float radius) Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); float d = 0.0f, dStep = 360.0f/sides; - for (int v = 0; v < vertexCount; v += 3) + for (int v = 0; v < vertexCount - 2; v += 3) { vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f }; vertices[v + 1] = (Vector3){ sinf(DEG2RAD*d)*radius, 0.0f, cosf(DEG2RAD*d)*radius }; - vertices[v + 2] = (Vector3){sinf(DEG2RAD*(d+dStep))*radius, 0.0f, cosf(DEG2RAD*(d+dStep))*radius }; + vertices[v + 2] = (Vector3){ sinf(DEG2RAD*(d+dStep))*radius, 0.0f, cosf(DEG2RAD*(d+dStep))*radius }; d += dStep; } diff --git a/src/rtext.c b/src/rtext.c index b83eb171b..dd7c83f39 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -2002,7 +2002,6 @@ int GetCodepointPrevious(const char *text, int *codepointSize) // Module specific Functions Definition //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_FNT) - // Read a line from memory // REQUIRES: memcpy() // NOTE: Returns the number of bytes read @@ -2032,7 +2031,9 @@ static Font LoadBMFont(const char *fileName) int imHeight = 0; char imFileName[129] = { 0 }; - int base = 0; // Useless data + int base = 0; // Useless data + int readBytes = 0; // Data bytes read + int readVars = 0; // Variables filled by sscanf() char *fileText = LoadFileText(fileName); @@ -2041,32 +2042,30 @@ static Font LoadBMFont(const char *fileName) char *fileTextPtr = fileText; // NOTE: We skip first line, it contains no useful information - int lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); - fileTextPtr += (lineBytes + 1); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + fileTextPtr += (readBytes + 1); // Read line data - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); searchPoint = strstr(buffer, "lineHeight"); - sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight); - fileTextPtr += (lineBytes + 1); + readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight); + fileTextPtr += (readBytes + 1); + + if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed - TRACELOGD("FONT: [%s] Loaded font info:", fileName); - TRACELOGD(" > Base size: %i", fontSize); - TRACELOGD(" > Texture scale: %ix%i", imWidth, imHeight); - - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); searchPoint = strstr(buffer, "file"); - sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName); - fileTextPtr += (lineBytes + 1); + readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName); + fileTextPtr += (readBytes + 1); - TRACELOGD(" > Texture filename: %s", imFileName); + if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); searchPoint = strstr(buffer, "count"); - sscanf(searchPoint, "count=%i", &glyphCount); - fileTextPtr += (lineBytes + 1); + readVars = sscanf(searchPoint, "count=%i", &glyphCount); + fileTextPtr += (readBytes + 1); - TRACELOGD(" > Chars count: %i", glyphCount); + if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read // Compose correct path using route of .fnt file (fileName) and imFileName char *imPath = NULL; @@ -2124,22 +2123,26 @@ static Font LoadBMFont(const char *fileName) for (int i = 0; i < glyphCount; i++) { - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); - sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); - fileTextPtr += (lineBytes + 1); + fileTextPtr += (readBytes + 1); + + if (readVars == 8) // Make sure all char data has been properly read + { + // Get character rectangle in the font atlas texture + font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; - // Get character rectangle in the font atlas texture - font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; + // Save data properly in sprite font + font.glyphs[i].value = charId; + font.glyphs[i].offsetX = charOffsetX; + font.glyphs[i].offsetY = charOffsetY; + font.glyphs[i].advanceX = charAdvanceX; - // Save data properly in sprite font - font.glyphs[i].value = charId; - font.glyphs[i].offsetX = charOffsetX; - font.glyphs[i].offsetY = charOffsetY; - font.glyphs[i].advanceX = charAdvanceX; - - // Fill character image data from imFont data - font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]); + // Fill character image data from imFont data + font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]); + } + else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName); } UnloadImage(imFont); diff --git a/src/rtextures.c b/src/rtextures.c index bf1b35e40..9465c5b39 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1288,8 +1288,6 @@ void ImageFormat(Image *image, int newFormat) image->data = NULL; image->format = newFormat; - int k = 0; - switch (image->format) { case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: @@ -1306,7 +1304,7 @@ void ImageFormat(Image *image, int newFormat) { image->data = (unsigned char *)RL_MALLOC(image->width*image->height*2*sizeof(unsigned char)); - for (int i = 0; i < image->width*image->height*2; i += 2, k++) + for (int i = 0, k = 0; i < image->width*image->height*2; i += 2, k++) { ((unsigned char *)image->data)[i] = (unsigned char)((pixels[k].x*0.299f + (float)pixels[k].y*0.587f + (float)pixels[k].z*0.114f)*255.0f); ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].w*255.0f); From c563490cf8370f5741e37bdd72d6b14c65240c8c Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Nov 2023 15:28:31 +0100 Subject: [PATCH 0155/1037] Update rcore_web.c --- src/platforms/rcore_web.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 959f60332..4d0bfcc21 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -46,7 +46,7 @@ **********************************************************************************************/ #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) -// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) +// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (translated to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Emscripten functionality for C From d77c918d51d3be901759d5f3755f7beee1469658 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Nov 2023 16:24:06 +0100 Subject: [PATCH 0156/1037] Update rcore_web.c --- src/platforms/rcore_web.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 4d0bfcc21..c5bd55369 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -45,8 +45,6 @@ * **********************************************************************************************/ -#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) -// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (translated to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Emscripten functionality for C From 35d27fb11b916d77075ec22dc07c4c411108d02d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 09:04:05 +0100 Subject: [PATCH 0157/1037] REVIEWED: Pointers exposing not required for ES3 --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 0c695c20c..8f2b5258f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1042,7 +1042,7 @@ typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions static rlglData RLGL = { 0 }; #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3) // NOTE: VAO functionality is exposed through extensions (OES) static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL; static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL; From de1ceae4b0ab5c68d75959490e9a81fa0c9f0593 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 2 Nov 2023 05:13:41 -0300 Subject: [PATCH 0158/1037] [core] Fix gestures for `PLATFORM_DESKTOP_SDL` (#3499) * Fix gestures for SDL * Review the gesture handling for SDL * Review 2 --- src/platforms/rcore_desktop_sdl.c | 48 ++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 7245b2d5c..91442d8e2 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -964,6 +964,15 @@ void PollInputEvents(void) // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + // Map touch position to mouse position for convenience + // WARNING: If the target desktop device supports touch screen, this behavious should be reviewed! + // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch + // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + + int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE + bool gestureUpdate = false; // Flag to note gestures require to update + // Register previous keys states // NOTE: Android supports up to 260 keys for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) @@ -1077,10 +1086,16 @@ void PollInputEvents(void) case SDL_MOUSEBUTTONDOWN: { CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 1; + + touchAction = 1; + gestureUpdate = true; } break; case SDL_MOUSEBUTTONUP: { CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 0; + + touchAction = 0; + gestureUpdate = true; } break; case SDL_MOUSEWHEEL: { @@ -1100,6 +1115,10 @@ void PollInputEvents(void) CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; } + + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + touchAction = 2; + gestureUpdate = true; } break; // Check gamepad events @@ -1122,11 +1141,38 @@ void PollInputEvents(void) } break; default: break; } + +#if defined(SUPPORT_GESTURES_SYSTEM) + if (gestureUpdate) + { + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + // Register touch actions + gestureEvent.touchAction = touchAction; + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + if (touchAction == 2) gestureEvent.position[0] = CORE.Input.Touch.position[0]; + else gestureEvent.position[0] = GetMousePosition(); + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); + } +#endif } //----------------------------------------------------------------------------- } - //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- From ba75a7a23bbfcae1747ab6a87ecb155979ce6bc5 Mon Sep 17 00:00:00 2001 From: Caleb Barger <59521636+cabarger@users.noreply.github.com> Date: Thu, 2 Nov 2023 01:14:34 -0700 Subject: [PATCH 0159/1037] build.zig updates for 0.11.0 release. (#3501) --- build.zig | 2 +- src/build.zig | 80 +++++++++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/build.zig b/build.zig index 21638601d..f4d7d1590 100644 --- a/build.zig +++ b/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig 0.11.0 pub fn build(b: *std.Build) void { raylib.build(b); } diff --git a/src/build.zig b/src/build.zig index 5e2b5916e..08f4b6bdf 100644 --- a/src/build.zig +++ b/src/build.zig @@ -20,55 +20,55 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rcore.c", srcdir ++ "/utils.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); if (options.raudio) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/raudio.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } if (options.rmodels) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rmodels.c", }, - .flags = &[_][]const u8{ + &[_][]const u8{ "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 } ++ raylib_flags, - }); + ); } if (options.rshapes) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rshapes.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } if (options.rtext) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rtext.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } if (options.rtextures) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rtextures.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } var gen_step = b.addWriteFiles(); @@ -83,10 +83,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built switch (target.getOsTag()) { .windows => { - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags, + ); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -96,10 +96,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, .linux => { if (!options.platform_drm) { - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags, + ); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -127,10 +127,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags, + ); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -149,10 +149,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const raylib_flags_extra_macos = &[_][]const u8{ "-ObjC", }; - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags ++ raylib_flags_extra_macos, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags ++ raylib_flags_extra_macos, + ); raylib.linkFramework("Foundation"); raylib.linkFramework("CoreServices"); raylib.linkFramework("CoreGraphics"); From fe34fc7c6b7e3731ce7f68b379f9847eaaaf2a8f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:35:11 -0300 Subject: [PATCH 0160/1037] Partial fix the gesture system for DRM (#3502) --- src/platforms/rcore_drm.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index c6307773f..ac5a1b6ca 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -170,6 +170,11 @@ static const int EvkeyToUnicodeLUT[] = { // LUT currently incomplete, just mapped the most essential keys }; +#if defined(SUPPORT_GESTURES_SYSTEM) +GestureEvent gestureEvent = { 0 }; // Gesture event to hold data between EventThread() and PollInputEvents() +bool newGesture = false; // Var to trigger ProcessGestureEvent(gestureEvent) on PollInputEvents() +#endif + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -592,6 +597,18 @@ void PollInputEvents(void) // Reset touch positions //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + // Map touch position to mouse position for convenience + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + +#if defined(SUPPORT_GESTURES_SYSTEM) + // Call the ProcessGestureEvent here instead of on EventThread() to workaround the threads not matching + if (newGesture) + { + ProcessGestureEvent(gestureEvent); + newGesture = false; + } +#endif + #if defined(SUPPORT_SSH_KEYBOARD_RPI) // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console @@ -603,7 +620,6 @@ void PollInputEvents(void) #endif } - //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- @@ -1715,7 +1731,7 @@ static void *EventThread(void *arg) #if defined(SUPPORT_GESTURES_SYSTEM) if (gestureUpdate) { - GestureEvent gestureEvent = { 0 }; + //GestureEvent gestureEvent = { 0 }; gestureEvent.touchAction = touchAction; gestureEvent.pointCount = CORE.Input.Touch.pointCount; @@ -1726,7 +1742,8 @@ static void *EventThread(void *arg) gestureEvent.position[i] = CORE.Input.Touch.position[i]; } - ProcessGestureEvent(gestureEvent); + //ProcessGestureEvent(gestureEvent); + newGesture = true; } #endif } From 01bbd425196631ea16e3c8fd4666f801fe8e4692 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:11:13 +0100 Subject: [PATCH 0161/1037] Update rprand.h --- src/external/rprand.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/external/rprand.h b/src/external/rprand.h index a9b3a6eaa..b8dc4a279 100644 --- a/src/external/rprand.h +++ b/src/external/rprand.h @@ -196,15 +196,14 @@ unsigned int *rprand_load_sequence(unsigned int count, int min, int max) sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); uint32_t value = 0; - int value_count = 0; bool value_is_dup = false; - for (int i = 0; value_count < count; i++) + for (int i = 0; i < count;) { value = rprand_xoshiro()%(max - min) + min; value_is_dup = false; - for (int j = 0; j < value_count; j++) + for (int j = 0; j < i; j++) { if (sequence[j] == value) { @@ -215,8 +214,8 @@ unsigned int *rprand_load_sequence(unsigned int count, int min, int max) if (!value_is_dup) { - sequence[value_count] = value; - value_count++; + sequence[i] = value; + i++; } } From b40f93b9e35ca8b2aa2e897eadb90187c2f93d4a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:12:22 +0100 Subject: [PATCH 0162/1037] Comments tweaks --- examples/Makefile | 32 +++++++++++++++---------------- src/Makefile | 32 +++++++++++++++---------------- src/platforms/rcore_desktop.c | 2 +- src/platforms/rcore_desktop_sdl.c | 3 +-- src/platforms/rcore_drm.c | 4 ++-- src/rcore.c | 32 +++++++++++++++---------------- 6 files changed, 52 insertions(+), 53 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index fe0ee9fbd..e126ab609 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -4,22 +4,22 @@ # # This file supports building raylib examples for the following platforms: # -# - PLATFORM_DESKTOP (GLFW backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > macOS/OSX (x64, arm64) -# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -# - PLATFORM_DESKTOP_SDL (SDL backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > Others (not tested) -# - PLATFORM_WEB: -# > HTML5 (WebAssembly) -# - PLATFORM_DRM: -# > Raspberry Pi 0-5 (no X11/Wayland) -# > Linux native mode (KMS driver) -# - PLATFORM_ANDROID: -# > Android (ARM, ARM64) +# > PLATFORM_DESKTOP (GLFW backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - macOS/OSX (x64, arm64) +# - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# > PLATFORM_DESKTOP_SDL (SDL backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - Others (not tested) +# > PLATFORM_WEB: +# - HTML5 (WebAssembly) +# > PLATFORM_DRM: +# - Raspberry Pi 0-5 (DRM/KMS) +# - Linux DRM subsystem (KMS mode) +# > PLATFORM_ANDROID: +# - Android (ARM, ARM64) # # Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) # diff --git a/src/Makefile b/src/Makefile index 2406588b8..7bcece7c8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,22 +4,22 @@ # # This file supports building raylib library for the following platforms: # -# - PLATFORM_DESKTOP (GLFW backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > macOS/OSX (x64, arm64) -# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -# - PLATFORM_DESKTOP_SDL (SDL backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > Others (not tested) -# - PLATFORM_WEB: -# > HTML5 (WebAssembly) -# - PLATFORM_DRM: -# > Raspberry Pi 0-5 (no X11/Wayland) -# > Linux native mode (KMS driver) -# - PLATFORM_ANDROID: -# > Android (ARM, ARM64) +# > PLATFORM_DESKTOP (GLFW backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - macOS/OSX (x64, arm64) +# - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# > PLATFORM_DESKTOP_SDL (SDL backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - Others (not tested) +# > PLATFORM_WEB: +# - HTML5 (WebAssembly) +# > PLATFORM_DRM: +# - Raspberry Pi 0-5 (DRM/KMS) +# - Linux DRM subsystem (KMS mode) +# > PLATFORM_ANDROID: +# - Android (ARM, ARM64) # # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 3d38dc898..426d9fc01 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -6,7 +6,7 @@ * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) * - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - OSX/macOS +* - OSX/macOS (x64, arm64) * * LIMITATIONS: * - Limitation 01 diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 91442d8e2..87ceacb86 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -5,8 +5,7 @@ * PLATFORM: DESKTOP: SDL * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) -* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - OSX/macOS (x64, arm64) +* - Others (not tested) * * LIMITATIONS: * - Limitation 01 diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index ac5a1b6ca..7bdc2d00d 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -3,8 +3,8 @@ * rcore_drm - Functions to manage window, graphics device and inputs * * PLATFORM: DRM -* - Raspberry Pi 0-5 (native mode) -* - Linux native mode (KMS driver) +* - Raspberry Pi 0-5 (DRM/KMS) +* - Linux DRM subsystem (KMS mode) * * LIMITATIONS: * - Most of the window/monitor functions are not implemented (not required) diff --git a/src/rcore.c b/src/rcore.c index d4e2573b0..e4d8eae5c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,22 +3,22 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP (GLFW backend): -* > Windows (Win32, Win64) -* > Linux (X11/Wayland desktop mode) -* > macOS/OSX (x64, arm64) -* > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - PLATFORM_DESKTOP_SDL (SDL backend): -* > Windows (Win32, Win64) -* > Linux (X11/Wayland desktop mode) -* > Others (not tested) -* - PLATFORM_WEB: -* > HTML5 (WebAssembly) -* - PLATFORM_DRM: -* > Raspberry Pi 0-5 -* > Linux native mode (KMS driver) -* - PLATFORM_ANDROID: -* > Android (ARM, ARM64) +* > PLATFORM_DESKTOP (GLFW backend): +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - macOS/OSX (x64, arm64) +* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* > PLATFORM_DESKTOP_SDL (SDL backend): +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - Others (not tested) +* > PLATFORM_WEB: +* - HTML5 (WebAssembly) +* > PLATFORM_DRM: +* - Raspberry Pi 0-5 (DRM/KMS) +* - Linux DRM subsystem (KMS mode) +* > PLATFORM_ANDROID: +* - Android (ARM, ARM64) * * CONFIGURATION: * #define SUPPORT_DEFAULT_FONT (default) From 807516a991deef0eae23980b9a829721028ab8c8 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:13:37 +0100 Subject: [PATCH 0163/1037] Support OpenGL ES 3.0 building on Web For some reason, the equivalent requested context (WebGL 2.0) is not provided, despite being properly requested. --- src/platforms/rcore_web.c | 5 ++++- src/rlgl.h | 17 ++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index c5bd55369..233616182 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -45,7 +45,9 @@ * **********************************************************************************************/ -#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management +#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) +#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Emscripten functionality for C #include // Emscripten HTML5 library @@ -759,6 +761,7 @@ int InitPlatform(void) } else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context { + // TODO: It seems WebGL 2.0 context is not set despite being requested glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); diff --git a/src/rlgl.h b/src/rlgl.h index 8f2b5258f..76704dfc2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -799,10 +799,14 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #define GLAD_FREE RL_FREE #define GLAD_GL_IMPLEMENTATION - #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers #endif -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES3) + #include // OpenGL ES 3.0 library + #define GL_GLEXT_PROTOTYPES + #include // OpenGL ES 2.0 extensions library +#elif defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) @@ -823,9 +827,6 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); #endif #endif -#if defined(GRAPHICS_API_OPENGL_ES3) - #include -#endif #include // Required for: malloc(), free() #include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] @@ -902,8 +903,10 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #if defined(GRAPHICS_API_OPENGL_ES2) #define glClearDepth glClearDepthf - #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER - #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER + #if !defined(GRAPHICS_API_OPENGL_ES3) + #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER + #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER + #endif #endif // Default shader vertex attribute names to set location points From 3c3e311190215451f31b7ea868f7950a8bed909f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:18:05 +0100 Subject: [PATCH 0164/1037] Remove unneeded line on web platform --- src/platforms/rcore_web.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 233616182..1fe4d5841 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -896,7 +896,6 @@ int InitPlatform(void) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions From 301d1b85ab62b13d072a0430668602398b382104 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:18:27 +0100 Subject: [PATCH 0165/1037] Update raylib version to **raylib 5.0** --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 3f6328ada..c732d8ef1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -84,7 +84,7 @@ #define RAYLIB_VERSION_MAJOR 5 #define RAYLIB_VERSION_MINOR 0 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "5.0-dev" +#define RAYLIB_VERSION "5.0" // Function specifiers in case library is build/used as a shared library (Windows) // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll From 5da0074fed50f3a63d3c47aa0f2e1fbdc9051059 Mon Sep 17 00:00:00 2001 From: AndreaBoroni <38136330+AndreaBoroni@users.noreply.github.com> Date: Fri, 3 Nov 2023 19:12:42 +0100 Subject: [PATCH 0166/1037] Fixed Issue 3504 (#3505) --- src/rcore.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index e4d8eae5c..ac5dea842 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2520,7 +2520,16 @@ void PlayAutomationEvent(AutomationEvent event) { // Input event case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break; // param[0]: key - case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[event.params[0]] = true; break; // param[0]: key + case INPUT_KEY_DOWN: { // param[0]: key + CORE.Input.Keyboard.currentKeyState[event.params[0]] = true; + if (CORE.Input.Keyboard.previousKeyState[event.params[0]] == false) { + if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) { + // Add character to the queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = event.params[0]; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + } + } break; case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break; // param[0]: key case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break; // param[0]: key case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y From 2d1b2119202d08245ca4d313b846a6d0a8b2139b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 Nov 2023 20:21:43 +0100 Subject: [PATCH 0167/1037] ADDED: `LoadRandomSequence()`/`UnloadRandomSequence()` --- src/external/rprand.h | 28 +++++++++++------------ src/raylib.h | 5 ++++- src/rcore.c | 52 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/external/rprand.h b/src/external/rprand.h index b8dc4a279..acdeac671 100644 --- a/src/external/rprand.h +++ b/src/external/rprand.h @@ -117,10 +117,10 @@ extern "C" { // Prevents name mangling of functions // Module Functions Declaration //---------------------------------------------------------------------------------- RPRANDAPI void rprand_set_seed(unsigned long long seed); // Set rprand_state for Xoshiro128**, seed is 64bit -RPRANDAPI unsigned int rprand_get_value(int min, int max); // Get random value within a range, min and max included +RPRANDAPI int rprand_get_value(int min, int max); // Get random value within a range, min and max included -RPRANDAPI unsigned int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates -RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo-random numbers sequence +RPRANDAPI int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates +RPRANDAPI void rprand_unload_sequence(int *sequence); // Unload pseudo-random numbers sequence #ifdef __cplusplus } @@ -136,7 +136,7 @@ RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo #if defined(RPRAND_IMPLEMENTATION) -#include // Required for: calloc(), free() +#include // Required for: calloc(), free(), abs() #include // Required for data types: uint32_t, uint64_t //---------------------------------------------------------------------------------- @@ -174,33 +174,33 @@ void rprand_set_seed(unsigned long long seed) } // Get random value within a range, min and max included -unsigned int rprand_get_value(int min, int max) +int rprand_get_value(int min, int max) { - unsigned int value = rprand_xoshiro()%(max - min) + min; + int value = rprand_xoshiro()%(abs(max - min) + 1) + min; return value; } -// Load pseudo-random numbers sequence with no duplicates -unsigned int *rprand_load_sequence(unsigned int count, int min, int max) +// Load pseudo-random numbers sequence with no duplicates, min and max included +int *rprand_load_sequence(unsigned int count, int min, int max) { - unsigned int *sequence = NULL; + int *sequence = NULL; - if (count > (max - min)) + if (count > (abs(max - min) + 1)) { RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n"); //count = (max - min); return sequence; } - sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); + sequence = (int *)RPRAND_CALLOC(count, sizeof(int)); - uint32_t value = 0; + int value = 0; bool value_is_dup = false; for (int i = 0; i < count;) { - value = rprand_xoshiro()%(max - min) + min; + value = (rprand_xoshiro()%(abs(max - min) + 1)) + min; value_is_dup = false; for (int j = 0; j < i; j++) @@ -223,7 +223,7 @@ unsigned int *rprand_load_sequence(unsigned int count, int min, int max) } // Unload pseudo-random numbers sequence -void rprand_unload_sequence(unsigned int *sequence) +void rprand_unload_sequence(int *sequence) { RPRAND_FREE(sequence); sequence = NULL; diff --git a/src/raylib.h b/src/raylib.h index c732d8ef1..4a353a70f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1067,10 +1067,13 @@ RLAPI void SwapScreenBuffer(void); // Swap back b RLAPI void PollInputEvents(void); // Register all input events RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) -// Misc. functions +// Random values generation functions RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) +RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated +RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence +// Misc. functions RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) diff --git a/src/rcore.c b/src/rcore.c index ac5dea842..b09dffe67 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1677,7 +1677,7 @@ void SetRandomSeed(unsigned int seed) #endif } -// Get a random value between min and max (both included) +// Get a random value between min and max included int GetRandomValue(int min, int max) { int value = 0; @@ -1695,6 +1695,7 @@ int GetRandomValue(int min, int max) // WARNING: Ranges higher than RAND_MAX will return invalid results // More specifically, if (max - min) > INT_MAX there will be an overflow, // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold + // NOTE: Depending on the library it can be as low as 32767 if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); @@ -1705,6 +1706,55 @@ int GetRandomValue(int min, int max) return value; } +// Load random values sequence, no values repeated, min and max included +int *LoadRandomSequence(unsigned int count, int min, int max) +{ + int *values = NULL; + +#if defined(SUPPORT_RPRAND_GENERATOR) + rprand_load_sequence(count, min, max); +#else + if (count > (abs(max - min) + 1)) return values; + + values = (int *)RL_CALLOC(count, sizeof(int)); + + int value = 0; + bool dupValue = false; + + for (int i = 0; i < count;) + { + value = (rand()%(abs(max - min) + 1) + min); + dupValue = false; + + for (int j = 0; j < i; j++) + { + if (values[j] == value) + { + dupValue = true; + break; + } + } + + if (!dupValue) + { + values[i] = value; + i++; + } + } +#endif + return values; +} + +// Unload random values sequence +void UnloadRandomSequence(int *sequence) +{ +#if defined(SUPPORT_RPRAND_GENERATOR) + rprand_unload_sequence(sequence); +#else + RL_FREE(sequence); +#endif +} + // Takes a screenshot of current screen (saved a .png) void TakeScreenshot(const char *fileName) { From 32e4be6fb9ec23b325d64b651a945a234351bb8f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 Nov 2023 22:23:50 +0100 Subject: [PATCH 0168/1037] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index b09dffe67..9a9ac3f44 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1712,7 +1712,7 @@ int *LoadRandomSequence(unsigned int count, int min, int max) int *values = NULL; #if defined(SUPPORT_RPRAND_GENERATOR) - rprand_load_sequence(count, min, max); + values = rprand_load_sequence(count, min, max); #else if (count > (abs(max - min) + 1)) return values; From 39b539c42f61270631c501e481076902b7f8fd7c Mon Sep 17 00:00:00 2001 From: freakmangd <53349189+freakmangd@users.noreply.github.com> Date: Sat, 4 Nov 2023 16:05:21 -0400 Subject: [PATCH 0169/1037] Allow raylib to be built with zig 0.11.0 and zig 0.12.0dev (master) (#3506) * allow zig 0.11.0 and 0.12.0dev to build raylib * update comment --- src/build.zig | 102 +++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/src/build.zig b/src/build.zig index 08f4b6bdf..12d4a7a58 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); +const builtin = @import("builtin"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig 0.11.0 and zig 0.12.0-dev.1390+94cee4fb2 pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", @@ -20,55 +21,37 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rcore.c", - srcdir ++ "/utils.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rcore.c", + srcdir ++ "/utils.c", + }, raylib_flags); if (options.raudio) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/raudio.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/raudio.c", + }, raylib_flags); } if (options.rmodels) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rmodels.c", - }, - &[_][]const u8{ - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 - } ++ raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rmodels.c", + }, &[_][]const u8{ + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 + } ++ raylib_flags); } if (options.rshapes) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rshapes.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rshapes.c", + }, raylib_flags); } if (options.rtext) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rtext.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rtext.c", + }, raylib_flags); } if (options.rtextures) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rtextures.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rtextures.c", + }, raylib_flags); } var gen_step = b.addWriteFiles(); @@ -83,10 +66,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built switch (target.getOsTag()) { .windows => { - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -96,10 +78,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, .linux => { if (!options.platform_drm) { - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -127,10 +108,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -149,10 +129,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const raylib_flags_extra_macos = &[_][]const u8{ "-ObjC", }; - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags ++ raylib_flags_extra_macos, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags ++ raylib_flags_extra_macos); raylib.linkFramework("Foundation"); raylib.linkFramework("CoreServices"); raylib.linkFramework("CoreGraphics"); @@ -235,3 +214,14 @@ const srcdir = struct { return std.fs.path.dirname(@src().file).?; } }.getSrcDir(); + +fn addCSourceFilesVersioned(exe: *std.Build.Step.Compile, files: []const []const u8, flags: []const []const u8) void { + if (comptime builtin.zig_version.minor >= 12) { + exe.addCSourceFiles(.{ + .files = files, + .flags = flags, + }); + } else { + exe.addCSourceFiles(files, flags); + } +} From cbf0324c3469df0303f2b00ed67b194a9dd98f0a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 12:18:54 +0100 Subject: [PATCH 0170/1037] Updating web shells open-graph info --- src/minshell.html | 7 ++++--- src/shell.html | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/minshell.html b/src/minshell.html index 2aa75578f..7a944b7b9 100644 --- a/src/minshell.html +++ b/src/minshell.html @@ -8,10 +8,11 @@ - + + @@ -20,12 +21,12 @@ - + - + diff --git a/src/shell.html b/src/shell.html index b373394bb..d4ef3a03c 100644 --- a/src/shell.html +++ b/src/shell.html @@ -8,10 +8,11 @@ - + + @@ -20,12 +21,12 @@ - + - + From 53451e98a72934c8619afc5e934b95a9e5fd9c1c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 13:02:29 +0100 Subject: [PATCH 0171/1037] Updated open graph for examples --- src/minshell.html | 4 ++-- src/shell.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/minshell.html b/src/minshell.html index 7a944b7b9..e18974ba1 100644 --- a/src/minshell.html +++ b/src/minshell.html @@ -15,8 +15,8 @@ - - + + diff --git a/src/shell.html b/src/shell.html index d4ef3a03c..15c3f39c0 100644 --- a/src/shell.html +++ b/src/shell.html @@ -15,8 +15,8 @@ - - + + From 17a968d2ed327eeaac406b9c123b70f14963cc81 Mon Sep 17 00:00:00 2001 From: glowiak <52356948+glowiak@users.noreply.github.com> Date: Sun, 5 Nov 2023 15:58:48 +0100 Subject: [PATCH 0172/1037] Jaylib is now up-to-date (#3508) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 400bc3846..63c9566d9 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -34,7 +34,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-hx | 4.2 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | 4.5 | [Janet](https://janet-lang.org/) | MIT | https://github.com/janet-lang/jaylib | -| jaylib | 4.2 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | +| jaylib | **4.5** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | | raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | From 127d69e8870ab4cf3269b367191f3655f472794c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:24:45 +0100 Subject: [PATCH 0173/1037] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index fadc3b659..4604abff6 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html) [![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows) [![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux) [![macOS](https://github.com/raysan5/raylib/workflows/macOS/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AmacOS) -[![Android](https://github.com/raysan5/raylib/workflows/Android/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AAndroid) [![WebAssembly](https://github.com/raysan5/raylib/workflows/WebAssembly/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWebAssembly) [![CMakeBuilds](https://github.com/raysan5/raylib/workflows/CMakeBuilds/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ACMakeBuilds) From 57f77c38589b3ee84b8fbeafef54fd307d41fc0c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:39:57 +0100 Subject: [PATCH 0174/1037] Update qoi.h --- src/external/qoi.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/external/qoi.h b/src/external/qoi.h index 6734ac46e..f2800b0cc 100644 --- a/src/external/qoi.h +++ b/src/external/qoi.h @@ -594,7 +594,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { FILE *f = fopen(filename, "wb"); - int size; + int size, err; void *encoded; if (!f) { @@ -608,10 +608,12 @@ int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { } fwrite(encoded, 1, size, f); + fflush(f); + err = ferror(f); fclose(f); QOI_FREE(encoded); - return size; + return err ? 0 : size; } void *qoi_read(const char *filename, qoi_desc *desc, int channels) { @@ -625,11 +627,10 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { fseek(f, 0, SEEK_END); size = ftell(f); - if (size <= 0) { + if (size <= 0 || fseek(f, 0, SEEK_SET) != 0) { fclose(f); return NULL; } - fseek(f, 0, SEEK_SET); data = QOI_MALLOC(size); if (!data) { @@ -639,8 +640,7 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { bytes_read = fread(data, 1, size, f); fclose(f); - - pixels = qoi_decode(data, bytes_read, desc, channels); + pixels = (bytes_read != size) ? NULL : qoi_decode(data, bytes_read, desc, channels); QOI_FREE(data); return pixels; } From 6ebfc6023b24c297de3286a7fd8b0ffdcffc4c1a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:40:00 +0100 Subject: [PATCH 0175/1037] Update rl_gputex.h --- src/external/rl_gputex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index fb7b5e8de..2cbe59636 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rl_gputex - GPU compressed textures loading and saving +* rl_gputex v1.0 - GPU compressed textures loading and saving * * DESCRIPTION: * From 925978ffde371d5cc1b049d8cf7b861ebd6bb2b5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:41:22 +0100 Subject: [PATCH 0176/1037] Update qoa.h --- src/external/qoa.h | 170 +++++++++++++++++++++++++++++++-------------- 1 file changed, 119 insertions(+), 51 deletions(-) diff --git a/src/external/qoa.h b/src/external/qoa.h index 59d90aded..fc62f4765 100644 --- a/src/external/qoa.h +++ b/src/external/qoa.h @@ -8,71 +8,96 @@ QOA - The "Quite OK Audio" format for fast, lossy audio compression -- Data Format -A QOA file has an 8 byte file header, followed by a number of frames. Each frame -consists of an 8 byte frame header, the current 8 byte en-/decoder state per -channel and 256 slices per channel. Each slice is 8 bytes wide and encodes 20 -samples of audio data. +QOA encodes pulse-code modulated (PCM) audio data with up to 255 channels, +sample rates from 1 up to 16777215 hertz and a bit depth of 16 bits. -Note that the last frame of a file may contain less than 256 slices per channel. -The last slice (per channel) in the last frame may contain less 20 samples, but -the slice will still be 8 bytes wide, with the unused samples zeroed out. +The compression method employed in QOA is lossy; it discards some information +from the uncompressed PCM data. For many types of audio signals this compression +is "transparent", i.e. the difference from the original file is often not +audible. -The samplerate and number of channels is only stated in the frame headers, but -not in the file header. A decoder may peek into the first frame of the file to -find these values. +QOA encodes 20 samples of 16 bit PCM data into slices of 64 bits. A single +sample therefore requires 3.2 bits of storage space, resulting in a 5x +compression (16 / 3.2). -In a valid QOA file all frames have the same number of channels and the same -samplerate. These restrictions may be relaxed for streaming. This remains to -be decided. +A QOA file consists of an 8 byte file header, followed by a number of frames. +Each frame contains an 8 byte frame header, the current 16 byte en-/decoder +state per channel and 256 slices per channel. Each slice is 8 bytes wide and +encodes 20 samples of audio data. -All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file, -including the headers, is 64 bit aligned, so it's possible to read files with -just a read_u64() that does the byte swapping if necessary. - -In pseudocode, the file layout is as follows: +All values, including the slices, are big endian. The file layout is as follows: struct { struct { - char magic[4]; // magic bytes 'qoaf' - uint32_t samples; // number of samples per channel in this file - } file_header; // = 64 bits + char magic[4]; // magic bytes "qoaf" + uint32_t samples; // samples per channel in this file + } file_header; struct { struct { - uint8_t num_channels; // number of channels + uint8_t num_channels; // no. of channels uint24_t samplerate; // samplerate in hz - uint16_t fsamples; // sample count per channel in this frame - uint16_t fsize; // frame size (including the frame header) - } frame_header; // = 64 bits + uint16_t fsamples; // samples per channel in this frame + uint16_t fsize; // frame size (includes this header) + } frame_header; struct { - int16_t history[4]; // = 64 bits - int16_t weights[4]; // = 64 bits + int16_t history[4]; // most recent last + int16_t weights[4]; // most recent last } lms_state[num_channels]; - qoa_slice_t slices[256][num_channels]; // = 64 bits each - } frames[samples * channels / qoa_max_framesize()]; -} qoa_file; + qoa_slice_t slices[256][num_channels]; -Wheras the 64bit qoa_slice_t is defined as follows: + } frames[ceil(samples / (256 * 20))]; +} qoa_file_t; + +Each `qoa_slice_t` contains a quantized scalefactor `sf_quant` and 20 quantized +residuals `qrNN`: .- QOA_SLICE -- 64 bits, 20 samples --------------------------/ /------------. | Byte[0] | Byte[1] | Byte[2] \ \ Byte[7] | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 / / 2 1 0 | |------------+--------+--------+--------+---------+---------+-\ \--+---------| -| sf_index | r00 | r01 | r02 | r03 | r04 | / / | r19 | +| sf_quant | qr00 | qr01 | qr02 | qr03 | qr04 | / / | qr19 | `-------------------------------------------------------------\ \------------` -`sf_index` defines the scalefactor to use for this slice as an index into the -qoa_scalefactor_tab[16] +Each frame except the last must contain exactly 256 slices per channel. The last +frame may contain between 1 .. 256 (inclusive) slices per channel. The last +slice (for each channel) in the last frame may contain less than 20 samples; the +slice still must be 8 bytes wide, with the unused samples zeroed out. -`r00`--`r19` are the residuals for the individual samples, divided by the -scalefactor and quantized by the qoa_quant_tab[]. +Channels are interleaved per slice. E.g. for 2 channel stereo: +slice[0] = L, slice[1] = R, slice[2] = L, slice[3] = R ... -In the decoder, a prediction of the next sample is computed by multiplying the -state (the last four output samples) with the predictor. The residual from the -slice is then dequantized using the qoa_dequant_tab[] and added to the -prediction. The result is clamped to int16 to form the final output sample. +A valid QOA file or stream must have at least one frame. Each frame must contain +at least one channel and one sample with a samplerate between 1 .. 16777215 +(inclusive). + +If the total number of samples is not known by the encoder, the samples in the +file header may be set to 0x00000000 to indicate that the encoder is +"streaming". In a streaming context, the samplerate and number of channels may +differ from frame to frame. For static files (those with samples set to a +non-zero value), each frame must have the same number of channels and same +samplerate. + +Note that this implementation of QOA only handles files with a known total +number of samples. + +A decoder should support at least 8 channels. The channel layout for channel +counts 1 .. 8 is: + + 1. Mono + 2. L, R + 3. L, R, C + 4. FL, FR, B/SL, B/SR + 5. FL, FR, C, B/SL, B/SR + 6. FL, FR, C, LFE, B/SL, B/SR + 7. FL, FR, C, LFE, B, SL, SR + 8. FL, FR, C, LFE, BL, BR, SL, SR + +QOA predicts each audio sample based on the previously decoded ones using a +"Sign-Sign Least Mean Squares Filter" (LMS). This prediction plus the +dequantized residual forms the final output sample. */ @@ -158,7 +183,7 @@ the higher end. Note that the residual zero is identical to the lowest positive value. This is mostly fine, since the qoa_div() function always rounds away from zero. */ -static int qoa_quant_tab[17] = { +static const int qoa_quant_tab[17] = { 7, 7, 7, 5, 5, 3, 3, 1, /* -8..-1 */ 0, /* 0 */ 0, 2, 2, 4, 4, 6, 6, 6 /* 1.. 8 */ @@ -169,13 +194,13 @@ static int qoa_quant_tab[17] = { less accurate at the higher end. In theory, the highest scalefactor that we would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we rely on the LMS filter to predict samples accurately enough that a maximum -residual of one quarter of the 16 bit range is high sufficient. I.e. with the +residual of one quarter of the 16 bit range is sufficient. I.e. with the scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14. The scalefactor values are computed as: scalefactor_tab[s] <- round(pow(s + 1, 2.75)) */ -static int qoa_scalefactor_tab[16] = { +static const int qoa_scalefactor_tab[16] = { 1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048 }; @@ -188,7 +213,7 @@ do this in .16 fixed point with integers, instead of floats. The reciprocal_tab is computed as: reciprocal_tab[s] <- ((1<<16) + scalefactor_tab[s] - 1) / scalefactor_tab[s] */ -static int qoa_reciprocal_tab[16] = { +static const int qoa_reciprocal_tab[16] = { 65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32 }; @@ -200,9 +225,13 @@ Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4 instead of 1. The dequant_tab assumes the following dequantized values for each of the quant_tab indices and is computed as: float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7}; -dequant_tab[s][q] <- round(scalefactor_tab[s] * dqt[q]) */ +dequant_tab[s][q] <- round_ties_away_from_zero(scalefactor_tab[s] * dqt[q]) -static int qoa_dequant_tab[16][8] = { +The rounding employed here is "to nearest, ties away from zero", i.e. positive +and negative values are treated symmetrically. +*/ + +static const int qoa_dequant_tab[16][8] = { { 1, -1, 3, -3, 5, -5, 7, -7}, { 5, -5, 18, -18, 32, -32, 49, -49}, { 16, -16, 53, -53, 95, -95, 147, -147}, @@ -270,7 +299,21 @@ static inline int qoa_div(int v, int scalefactor) { } static inline int qoa_clamp(int v, int min, int max) { - return (v < min) ? min : (v > max) ? max : v; + if (v < min) { return min; } + if (v > max) { return max; } + return v; +} + +/* This specialized clamp function for the signed 16 bit range improves decode +performance quite a bit. The extra if() statement works nicely with the CPUs +branch prediction as this branch is rarely taken. */ + +static inline int qoa_clamp_s16(int v) { + if ((unsigned int)(v + 32768) > 65535) { + if (v < -32768) { return -32768; } + if (v > 32767) { return 32767; } + } + return v; } static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) { @@ -312,6 +355,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned unsigned int p = 0; unsigned int slices = (frame_len + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN; unsigned int frame_size = QOA_FRAME_SIZE(channels, slices); + int prev_scalefactor[QOA_MAX_CHANNELS] = {0}; /* Write the frame header */ qoa_write_u64(( @@ -321,8 +365,24 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned (qoa_uint64_t)frame_size ), bytes, &p); - /* Write the current LMS state */ + for (int c = 0; c < channels; c++) { + /* If the weights have grown too large, reset them to 0. This may happen + with certain high-frequency sounds. This is a last resort and will + introduce quite a bit of noise, but should at least prevent pops/clicks */ + int weights_sum = + qoa->lms[c].weights[0] * qoa->lms[c].weights[0] + + qoa->lms[c].weights[1] * qoa->lms[c].weights[1] + + qoa->lms[c].weights[2] * qoa->lms[c].weights[2] + + qoa->lms[c].weights[3] * qoa->lms[c].weights[3]; + if (weights_sum > 0x2fffffff) { + qoa->lms[c].weights[0] = 0; + qoa->lms[c].weights[1] = 0; + qoa->lms[c].weights[2] = 0; + qoa->lms[c].weights[3] = 0; + } + + /* Write the current LMS state */ qoa_uint64_t weights = 0; qoa_uint64_t history = 0; for (int i = 0; i < QOA_LMS_LEN; i++) { @@ -348,8 +408,13 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned qoa_uint64_t best_error = -1; qoa_uint64_t best_slice; qoa_lms_t best_lms; + int best_scalefactor; - for (int scalefactor = 0; scalefactor < 16; scalefactor++) { + for (int sfi = 0; sfi < 16; sfi++) { + /* There is a strong correlation between the scalefactors of + neighboring slices. As an optimization, start testing + the best scalefactor of the previous slice first. */ + int scalefactor = (sfi + prev_scalefactor[c]) % 16; /* We have to reset the LMS state to the last known good one before trying each scalefactor, as each pass updates the LMS @@ -367,7 +432,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int clamped = qoa_clamp(scaled, -8, 8); int quantized = qoa_quant_tab[clamped + 8]; int dequantized = qoa_dequant_tab[scalefactor][quantized]; - int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); + int reconstructed = qoa_clamp_s16(predicted + dequantized); long long error = (sample - reconstructed); current_error += error * error; @@ -383,9 +448,12 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned best_error = current_error; best_slice = slice; best_lms = lms; + best_scalefactor = scalefactor; } } + prev_scalefactor[c] = best_scalefactor; + qoa->lms[c] = best_lms; #ifdef QOA_RECORD_TOTAL_ERROR qoa->error += best_error; @@ -553,7 +621,7 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa int predicted = qoa_lms_predict(&qoa->lms[c]); int quantized = (slice >> 57) & 0x7; int dequantized = qoa_dequant_tab[scalefactor][quantized]; - int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); + int reconstructed = qoa_clamp_s16(predicted + dequantized); sample_data[si] = reconstructed; slice <<= 3; From 2252f747b734e3f5f9b78a46aa1c8e667c44ab0c Mon Sep 17 00:00:00 2001 From: JupiterRider <60042618+JupiterRider@users.noreply.github.com> Date: Sun, 5 Nov 2023 19:42:56 +0100 Subject: [PATCH 0177/1037] Update Makefile (#3509) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 7bcece7c8..2ee0c5a06 100644 --- a/src/Makefile +++ b/src/Makefile @@ -602,7 +602,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) @echo "raylib library generated (lib$(RAYLIB_LIB_NAME).a)!" else ifeq ($(RAYLIB_LIBTYPE),SHARED) - ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL)) ifeq ($(PLATFORM_OS),WINDOWS) # NOTE: Linking with provided resource file $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/$(RAYLIB_LIB_NAME).dll $(OBJS) $(RAYLIB_RES_FILE) $(LDFLAGS) $(LDLIBS) From 343e92322bac845511651dc6e15a2e16fdeb94c1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:46:25 +0100 Subject: [PATCH 0178/1037] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4604abff6..a88e0f5ce 100644 --- a/README.md +++ b/README.md @@ -38,20 +38,20 @@ features - **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external) - Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!** - Written in plain C code (C99) using PascalCase/camelCase notation - - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3 or ES 2.0**) + - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0**) - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - - Multiple **Fonts** formats supported (TTF, Image fonts, AngelCode fonts) + - Multiple **Fonts** formats supported (TTF, OTF, Image fonts, AngelCode fonts) - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC) - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! - Flexible Materials system, supporting classic maps and **PBR maps** - - **Animated 3D models** supported (skeletal bones animation) (IQM) - - Shaders support, including model and **postprocessing** shaders. + - **Animated 3D models** supported (skeletal bones animation) (IQM, M3D, glTF) + - Shaders support, including model shaders and **postprocessing** shaders - **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h) - - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) + - Audio loading and playing with streaming support (WAV, QOA, OGG, MP3, FLAC, XM, MOD) - **VR stereo rendering** support with configurable HMD device parameters - - Huge examples collection with [+120 code examples](https://github.com/raysan5/raylib/tree/master/examples)! - - Bindings to [+60 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! - - **Free and open source**. + - Huge examples collection with [+130 code examples](https://github.com/raysan5/raylib/tree/master/examples)! + - Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! + - **Free and open source** basic example -------------- From 8b3c18de545eb63b7dc2d08621ba83a215a07463 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 20:11:14 +0100 Subject: [PATCH 0179/1037] Updated GLFW for 64bit --- examples/others/external/include/GLFW/glfw3.h | 1064 +++++++++++------ .../external/include/GLFW/glfw3native.h | 226 ++-- examples/others/external/lib/libglfw3.a | Bin 269591 -> 290550 bytes 3 files changed, 860 insertions(+), 430 deletions(-) diff --git a/examples/others/external/include/GLFW/glfw3.h b/examples/others/external/include/GLFW/glfw3.h index 990fe3f7f..31b201ae5 100644 --- a/examples/others/external/include/GLFW/glfw3.h +++ b/examples/others/external/include/GLFW/glfw3.h @@ -3,7 +3,7 @@ * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Löwy + * Copyright (c) 2006-2019 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -52,7 +52,7 @@ extern "C" { * This is the reference documentation for OpenGL and OpenGL ES context related * functions. For more task-oriented information, see the @ref context_guide. */ -/*! @defgroup vulkan Vulkan reference +/*! @defgroup vulkan Vulkan support reference * @brief Functions and types related to Vulkan. * * This is the reference documentation for Vulkan related functions and types. @@ -96,11 +96,30 @@ extern "C" { #define _WIN32 #endif /* _WIN32 */ +/* Include because most Windows GLU headers need wchar_t and + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +/* Include because it is needed by Vulkan and related functions. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +#if defined(GLFW_INCLUDE_VULKAN) + #include +#endif /* Vulkan header */ + +/* The Vulkan header may have indirectly included windows.h (because of + * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it. + */ + /* It is customary to use APIENTRY for OpenGL function pointer declarations on * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. */ -#ifndef APIENTRY - #ifdef _WIN32 +#if !defined(APIENTRY) + #if defined(_WIN32) #define APIENTRY __stdcall #else #define APIENTRY @@ -122,17 +141,6 @@ extern "C" { #define GLFW_CALLBACK_DEFINED #endif /* CALLBACK */ -/* Include because most Windows GLU headers need wchar_t and - * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. - * Include it unconditionally to avoid surprising side-effects. - */ -#include - -/* Include because it is needed by Vulkan and related functions. - * Include it unconditionally to avoid surprising side-effects. - */ -#include - /* Include the chosen OpenGL or OpenGL ES headers. */ #if defined(GLFW_INCLUDE_ES1) @@ -182,10 +190,44 @@ extern "C" { #else /*__APPLE__*/ #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif #endif /*__APPLE__*/ -#elif !defined(GLFW_INCLUDE_NONE) +#elif defined(GLFW_INCLUDE_GLU) + + #if defined(__APPLE__) + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #else /*__APPLE__*/ + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #endif /*__APPLE__*/ + +#elif !defined(GLFW_INCLUDE_NONE) && \ + !defined(__gl_h_) && \ + !defined(__gles1_gl_h_) && \ + !defined(__gles2_gl2_h_) && \ + !defined(__gles2_gl3_h_) && \ + !defined(__gles2_gl31_h_) && \ + !defined(__gles2_gl32_h_) && \ + !defined(__gl_glcorearb_h_) && \ + !defined(__gl2_h_) /*legacy*/ && \ + !defined(__gl3_h_) /*legacy*/ && \ + !defined(__gl31_h_) /*legacy*/ && \ + !defined(__gl32_h_) /*legacy*/ && \ + !defined(__glcorearb_h_) /*legacy*/ && \ + !defined(__GL_H__) /*non-standard*/ && \ + !defined(__gltypes_h_) /*non-standard*/ && \ + !defined(__glee_h_) /*non-standard*/ #if defined(__APPLE__) @@ -193,9 +235,6 @@ extern "C" { #define GL_GLEXT_LEGACY #endif #include - #if defined(GLFW_INCLUDE_GLU) - #include - #endif #else /*__APPLE__*/ @@ -203,18 +242,11 @@ extern "C" { #if defined(GLFW_INCLUDE_GLEXT) #include #endif - #if defined(GLFW_INCLUDE_GLU) - #include - #endif #endif /*__APPLE__*/ #endif /* OpenGL and OpenGL ES headers */ -#if defined(GLFW_INCLUDE_VULKAN) - #include -#endif /* Vulkan header */ - #if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) /* GLFW_DLL must be defined by applications that are linking against the DLL * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW @@ -230,13 +262,12 @@ extern "C" { /* We are building GLFW as a Win32 DLL */ #define GLFWAPI __declspec(dllexport) #elif defined(_WIN32) && defined(GLFW_DLL) - /* We are calling GLFW as a Win32 DLL */ + /* We are calling a GLFW Win32 DLL */ #define GLFWAPI __declspec(dllimport) #elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) - /* We are building GLFW as a shared / dynamic library */ + /* We are building GLFW as a Unix shared library */ #define GLFWAPI __attribute__((visibility("default"))) #else - /* We are building or calling GLFW as a static library */ #define GLFWAPI #endif @@ -247,45 +278,47 @@ extern "C" { /*! @name GLFW version macros * @{ */ -/*! @brief The major version number of the GLFW library. +/*! @brief The major version number of the GLFW header. * - * This is incremented when the API is changed in non-compatible ways. + * The major version number of the GLFW header. This is incremented when the + * API is changed in non-compatible ways. * @ingroup init */ #define GLFW_VERSION_MAJOR 3 -/*! @brief The minor version number of the GLFW library. +/*! @brief The minor version number of the GLFW header. * - * This is incremented when features are added to the API but it remains - * backward-compatible. + * The minor version number of the GLFW header. This is incremented when + * features are added to the API but it remains backward-compatible. * @ingroup init */ #define GLFW_VERSION_MINOR 3 -/*! @brief The revision number of the GLFW library. +/*! @brief The revision number of the GLFW header. * - * This is incremented when a bug fix release is made that does not contain any - * API changes. + * The revision number of the GLFW header. This is incremented when a bug fix + * release is made that does not contain any API changes. * @ingroup init */ -#define GLFW_VERSION_REVISION 0 +#define GLFW_VERSION_REVISION 8 /*! @} */ -/*! @name Boolean values - * @{ */ /*! @brief One. * - * One. Seriously. You don't _need_ to use this symbol in your code. It's - * semantic sugar for the number 1. You can also use `1` or `true` or `_True` - * or `GL_TRUE` or whatever you want. + * This is only semantic sugar for the number 1. You can instead use `1` or + * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal + * to one. + * + * @ingroup init */ #define GLFW_TRUE 1 /*! @brief Zero. * - * Zero. Seriously. You don't _need_ to use this symbol in your code. It's - * semantic sugar for the number 0. You can also use `0` or `false` or - * `_False` or `GL_FALSE` or whatever you want. + * This is only semantic sugar for the number 0. You can instead use `0` or + * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is + * equal to zero. + * + * @ingroup init */ #define GLFW_FALSE 0 -/*! @} */ /*! @name Key and button actions * @{ */ @@ -313,6 +346,7 @@ extern "C" { /*! @} */ /*! @defgroup hat_state Joystick hat states + * @brief Joystick hat states. * * See [joystick hat input](@ref joystick_hat) for how these are used. * @@ -915,70 +949,87 @@ extern "C" { #define GLFW_CLIENT_API 0x00022001 /*! @brief Context client API major version hint and attribute. * - * Context client API major version [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib). */ #define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 /*! @brief Context client API minor version hint and attribute. * - * Context client API minor version [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib). */ #define GLFW_CONTEXT_VERSION_MINOR 0x00022003 -/*! @brief Context client API revision number hint and attribute. +/*! @brief Context client API revision number attribute. * - * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API revision number + * [attribute](@ref GLFW_CONTEXT_REVISION_attrib). */ #define GLFW_CONTEXT_REVISION 0x00022004 /*! @brief Context robustness hint and attribute. * - * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint) + * and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib). */ #define GLFW_CONTEXT_ROBUSTNESS 0x00022005 /*! @brief OpenGL forward-compatibility hint and attribute. * - * OpenGL forward-compatibility [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) + * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib). */ #define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 -/*! @brief OpenGL debug context hint and attribute. +/*! @brief Debug mode context hint and attribute. * - * OpenGL debug context [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and + * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib). */ #define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 /*! @brief OpenGL profile hint and attribute. * - * OpenGL profile [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and + * [attribute](@ref GLFW_OPENGL_PROFILE_attrib). */ #define GLFW_OPENGL_PROFILE 0x00022008 /*! @brief Context flush-on-release hint and attribute. * - * Context flush-on-release [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and + * [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib). */ #define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 /*! @brief Context error suppression hint and attribute. * - * Context error suppression [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and + * [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib). */ #define GLFW_CONTEXT_NO_ERROR 0x0002200A /*! @brief Context creation API hint and attribute. * - * Context creation API [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and + * [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib). */ #define GLFW_CONTEXT_CREATION_API 0x0002200B - +/*! @brief Window content area scaling window + * [window hint](@ref GLFW_SCALE_TO_MONITOR). + */ +#define GLFW_SCALE_TO_MONITOR 0x0002200C +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). + */ #define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). + */ #define GLFW_COCOA_FRAME_NAME 0x00023002 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). + */ #define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 - +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ #define GLFW_X11_CLASS_NAME 0x00024001 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ #define GLFW_X11_INSTANCE_NAME 0x00024002 /*! @} */ @@ -998,6 +1049,7 @@ extern "C" { #define GLFW_STICKY_KEYS 0x00033002 #define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 #define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_RAW_MOUSE_MOTION 0x00033005 #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 @@ -1056,9 +1108,20 @@ extern "C" { /*! @addtogroup init * @{ */ +/*! @brief Joystick hat buttons init hint. + * + * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS). + */ #define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 - +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). + */ #define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). + */ #define GLFW_COCOA_MENUBAR 0x00051002 /*! @} */ @@ -1129,17 +1192,25 @@ typedef struct GLFWwindow GLFWwindow; * * @since Added in version 3.1. * - * @ingroup cursor + * @ingroup input */ typedef struct GLFWcursor GLFWcursor; -/*! @brief The function signature for error callbacks. +/*! @brief The function pointer type for error callbacks. * - * This is the function signature for error callback functions. + * This is the function pointer type for error callbacks. An error callback + * function has the following signature: + * @code + * void callback_name(int error_code, const char* description) + * @endcode * - * @param[in] error An [error code](@ref errors). + * @param[in] error_code An [error code](@ref errors). Future releases may add + * more error codes. * @param[in] description A UTF-8 encoded string describing the error. * + * @pointer_lifetime The error description string is valid until the callback + * function returns. + * * @sa @ref error_handling * @sa @ref glfwSetErrorCallback * @@ -1147,17 +1218,21 @@ typedef struct GLFWcursor GLFWcursor; * * @ingroup init */ -typedef void (* GLFWerrorfun)(int,const char*); +typedef void (* GLFWerrorfun)(int error_code, const char* description); -/*! @brief The function signature for window position callbacks. +/*! @brief The function pointer type for window position callbacks. * - * This is the function signature for window position callback functions. + * This is the function pointer type for window position callbacks. A window + * position callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int xpos, int ypos) + * @endcode * * @param[in] window The window that was moved. * @param[in] xpos The new x-coordinate, in screen coordinates, of the - * upper-left corner of the client area of the window. + * upper-left corner of the content area of the window. * @param[in] ypos The new y-coordinate, in screen coordinates, of the - * upper-left corner of the client area of the window. + * upper-left corner of the content area of the window. * * @sa @ref window_pos * @sa @ref glfwSetWindowPosCallback @@ -1166,11 +1241,15 @@ typedef void (* GLFWerrorfun)(int,const char*); * * @ingroup window */ -typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); +typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos); -/*! @brief The function signature for window resize callbacks. +/*! @brief The function pointer type for window size callbacks. * - * This is the function signature for window size callback functions. + * This is the function pointer type for window size callbacks. A window size + * callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int width, int height) + * @endcode * * @param[in] window The window that was resized. * @param[in] width The new width, in screen coordinates, of the window. @@ -1184,11 +1263,15 @@ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); +typedef void (* GLFWwindowsizefun)(GLFWwindow* window, int width, int height); -/*! @brief The function signature for window close callbacks. +/*! @brief The function pointer type for window close callbacks. * - * This is the function signature for window close callback functions. + * This is the function pointer type for window close callbacks. A window + * close callback function has the following signature: + * @code + * void function_name(GLFWwindow* window) + * @endcode * * @param[in] window The window that the user attempted to close. * @@ -1200,11 +1283,15 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowclosefun)(GLFWwindow*); +typedef void (* GLFWwindowclosefun)(GLFWwindow* window); -/*! @brief The function signature for window content refresh callbacks. +/*! @brief The function pointer type for window content refresh callbacks. * - * This is the function signature for window refresh callback functions. + * This is the function pointer type for window content refresh callbacks. + * A window content refresh callback function has the following signature: + * @code + * void function_name(GLFWwindow* window); + * @endcode * * @param[in] window The window whose content needs to be refreshed. * @@ -1216,11 +1303,15 @@ typedef void (* GLFWwindowclosefun)(GLFWwindow*); * * @ingroup window */ -typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); +typedef void (* GLFWwindowrefreshfun)(GLFWwindow* window); -/*! @brief The function signature for window focus/defocus callbacks. +/*! @brief The function pointer type for window focus callbacks. * - * This is the function signature for window focus callback functions. + * This is the function pointer type for window focus callbacks. A window + * focus callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode * * @param[in] window The window that gained or lost input focus. * @param[in] focused `GLFW_TRUE` if the window was given input focus, or @@ -1233,12 +1324,15 @@ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); * * @ingroup window */ -typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); +typedef void (* GLFWwindowfocusfun)(GLFWwindow* window, int focused); -/*! @brief The function signature for window iconify/restore callbacks. +/*! @brief The function pointer type for window iconify callbacks. * - * This is the function signature for window iconify/restore callback - * functions. + * This is the function pointer type for window iconify callbacks. A window + * iconify callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode * * @param[in] window The window that was iconified or restored. * @param[in] iconified `GLFW_TRUE` if the window was iconified, or @@ -1251,15 +1345,18 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); +typedef void (* GLFWwindowiconifyfun)(GLFWwindow* window, int iconified); -/*! @brief The function signature for window maximize/restore callbacks. +/*! @brief The function pointer type for window maximize callbacks. * - * This is the function signature for window maximize/restore callback - * functions. + * This is the function pointer type for window maximize callbacks. A window + * maximize callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode * * @param[in] window The window that was maximized or restored. - * @param[in] iconified `GLFW_TRUE` if the window was maximized, or + * @param[in] maximized `GLFW_TRUE` if the window was maximized, or * `GLFW_FALSE` if it was restored. * * @sa @ref window_maximize @@ -1269,12 +1366,15 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); +typedef void (* GLFWwindowmaximizefun)(GLFWwindow* window, int maximized); -/*! @brief The function signature for framebuffer resize callbacks. +/*! @brief The function pointer type for framebuffer size callbacks. * - * This is the function signature for framebuffer resize callback - * functions. + * This is the function pointer type for framebuffer size callbacks. + * A framebuffer size callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode * * @param[in] window The window whose framebuffer was resized. * @param[in] width The new width, in pixels, of the framebuffer. @@ -1287,12 +1387,15 @@ typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); +typedef void (* GLFWframebuffersizefun)(GLFWwindow* window, int width, int height); -/*! @brief The function signature for window content scale callbacks. +/*! @brief The function pointer type for window content scale callbacks. * - * This is the function signature for window content scale callback - * functions. + * This is the function pointer type for window content scale callbacks. + * A window content scale callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode * * @param[in] window The window whose content scale changed. * @param[in] xscale The new x-axis content scale of the window. @@ -1305,16 +1408,21 @@ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); +typedef void (* GLFWwindowcontentscalefun)(GLFWwindow* window, float xscale, float yscale); -/*! @brief The function signature for mouse button callbacks. +/*! @brief The function pointer type for mouse button callbacks. * - * This is the function signature for mouse button callback functions. + * This is the function pointer type for mouse button callback functions. + * A mouse button callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode * * @param[in] window The window that received the event. * @param[in] button The [mouse button](@ref buttons) that was pressed or * released. - * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases + * may add more actions. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * @@ -1326,17 +1434,21 @@ typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); * * @ingroup input */ -typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); +typedef void (* GLFWmousebuttonfun)(GLFWwindow* window, int button, int action, int mods); -/*! @brief The function signature for cursor position callbacks. +/*! @brief The function pointer type for cursor position callbacks. * - * This is the function signature for cursor position callback functions. + * This is the function pointer type for cursor position callbacks. A cursor + * position callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode * * @param[in] window The window that received the event. * @param[in] xpos The new cursor x-coordinate, relative to the left edge of - * the client area. + * the content area. * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the - * client area. + * content area. * * @sa @ref cursor_pos * @sa @ref glfwSetCursorPosCallback @@ -1345,14 +1457,18 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); * * @ingroup input */ -typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); +typedef void (* GLFWcursorposfun)(GLFWwindow* window, double xpos, double ypos); -/*! @brief The function signature for cursor enter/leave callbacks. +/*! @brief The function pointer type for cursor enter/leave callbacks. * - * This is the function signature for cursor enter/leave callback functions. + * This is the function pointer type for cursor enter/leave callbacks. + * A cursor enter/leave callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode * * @param[in] window The window that received the event. - * @param[in] entered `GLFW_TRUE` if the cursor entered the window's client + * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content * area, or `GLFW_FALSE` if it left it. * * @sa @ref cursor_enter @@ -1362,11 +1478,15 @@ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); * * @ingroup input */ -typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); +typedef void (* GLFWcursorenterfun)(GLFWwindow* window, int entered); -/*! @brief The function signature for scroll callbacks. +/*! @brief The function pointer type for scroll callbacks. * - * This is the function signature for scroll callback functions. + * This is the function pointer type for scroll callbacks. A scroll callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode * * @param[in] window The window that received the event. * @param[in] xoffset The scroll offset along the x-axis. @@ -1379,16 +1499,21 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); * * @ingroup input */ -typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); +typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffset); -/*! @brief The function signature for keyboard key callbacks. +/*! @brief The function pointer type for keyboard key callbacks. * - * This is the function signature for keyboard key callback functions. + * This is the function pointer type for keyboard key callbacks. A keyboard + * key callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode * * @param[in] window The window that received the event. * @param[in] key The [keyboard key](@ref keys) that was pressed or released. * @param[in] scancode The system-specific scancode of the key. - * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. + * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future + * releases may add more actions. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * @@ -1400,11 +1525,15 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); * * @ingroup input */ -typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); +typedef void (* GLFWkeyfun)(GLFWwindow* window, int key, int scancode, int action, int mods); -/*! @brief The function signature for Unicode character callbacks. +/*! @brief The function pointer type for Unicode character callbacks. * - * This is the function signature for Unicode character callback functions. + * This is the function pointer type for Unicode character callbacks. + * A Unicode character callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode * * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. @@ -1417,14 +1546,18 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); * * @ingroup input */ -typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); +typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint); -/*! @brief The function signature for Unicode character with modifiers +/*! @brief The function pointer type for Unicode character with modifiers * callbacks. * - * This is the function signature for Unicode character with modifiers callback - * functions. It is called for each input character, regardless of what - * modifier keys are held down. + * This is the function pointer type for Unicode character with modifiers + * callbacks. It is called for each input character, regardless of what + * modifier keys are held down. A Unicode character with modifiers callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode * * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. @@ -1440,16 +1573,23 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); * * @ingroup input */ -typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); +typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods); -/*! @brief The function signature for file drop callbacks. +/*! @brief The function pointer type for path drop callbacks. * - * This is the function signature for file drop callbacks. + * This is the function pointer type for path drop callbacks. A path drop + * callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode * * @param[in] window The window that received the event. - * @param[in] count The number of dropped files. + * @param[in] path_count The number of dropped paths. * @param[in] paths The UTF-8 encoded file and/or directory path names. * + * @pointer_lifetime The path array and its strings are valid until the + * callback function returns. + * * @sa @ref path_drop * @sa @ref glfwSetDropCallback * @@ -1457,15 +1597,19 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); * * @ingroup input */ -typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); +typedef void (* GLFWdropfun)(GLFWwindow* window, int path_count, const char* paths[]); -/*! @brief The function signature for monitor configuration callbacks. +/*! @brief The function pointer type for monitor configuration callbacks. * - * This is the function signature for monitor configuration callback functions. + * This is the function pointer type for monitor configuration callbacks. + * A monitor callback function has the following signature: + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode * * @param[in] monitor The monitor that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining - * values reserved for future use. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. * * @sa @ref monitor_event * @sa @ref glfwSetMonitorCallback @@ -1474,16 +1618,19 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); * * @ingroup monitor */ -typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); +typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event); -/*! @brief The function signature for joystick configuration callbacks. +/*! @brief The function pointer type for joystick configuration callbacks. * - * This is the function signature for joystick configuration callback - * functions. + * This is the function pointer type for joystick configuration callbacks. + * A joystick configuration callback function has the following signature: + * @code + * void function_name(int jid, int event) + * @endcode * * @param[in] jid The joystick that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining - * values reserved for future use. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. * * @sa @ref joystick_event * @sa @ref glfwSetJoystickCallback @@ -1492,7 +1639,7 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); * * @ingroup input */ -typedef void (* GLFWjoystickfun)(int,int); +typedef void (* GLFWjoystickfun)(int jid, int event); /*! @brief Video mode type. * @@ -1567,6 +1714,8 @@ typedef struct GLFWgammaramp * * @since Added in version 2.1. * @glfw3 Removed format and bytes-per-pixel members. + * + * @ingroup window */ typedef struct GLFWimage { @@ -1589,6 +1738,8 @@ typedef struct GLFWimage * @sa @ref glfwGetGamepadState * * @since Added in version 3.3. + * + * @ingroup input */ typedef struct GLFWgamepadstate { @@ -1630,6 +1781,10 @@ typedef struct GLFWgamepadstate * bundle, if present. This can be disabled with the @ref * GLFW_COCOA_CHDIR_RESOURCES init hint. * + * @remark @x11 This function will set the `LC_CTYPE` category of the + * application locale according to the current environment if that category is + * still "C". This is because the "C" locale breaks Unicode text input. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init @@ -1653,6 +1808,8 @@ GLFWAPI int glfwInit(void); * call this function, as it is called by @ref glfwInit before it returns * failure. * + * This function has no effect if GLFW is not initialized. + * * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * * @remark This function may be called before @ref glfwInit. @@ -1814,10 +1971,17 @@ GLFWAPI int glfwGetError(const char** description); * Once set, the error callback remains set even after the library has been * terminated. * - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set. * + * @callback_signature + * @code + * void callback_name(int error_code, const char* description) + * @endcode + * For more information about the callback parameters, see the + * [callback pointer type](@ref GLFWerrorfun). + * * @errors None. * * @remark This function may be called before @ref glfwInit. @@ -1831,7 +1995,7 @@ GLFWAPI int glfwGetError(const char** description); * * @ingroup init */ -GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback); /*! @brief Returns the currently connected monitors. * @@ -1911,6 +2075,37 @@ GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); */ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); +/*! @brief Retrieves the work area of the monitor. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the work area of the specified monitor along with the work area + * size in screen coordinates. The work area is defined as the area of the + * monitor not occluded by the operating system task bar where present. If no + * task bar exists then the work area is the monitor resolution in screen + * coordinates. + * + * Any or all of the position and size arguments may be `NULL`. If an error + * occurs, all non-`NULL` position and size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * @param[out] width Where to store the monitor width, or `NULL`. + * @param[out] height Where to store the monitor height, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_workarea + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); + /*! @brief Returns the physical size of the monitor. * * This function returns the size, in millimetres, of the display area of the @@ -1932,8 +2127,8 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @win32 calculates the returned physical size from the - * current resolution and system DPI instead of querying the monitor EDID data. + * @remark @win32 On Windows 8 and earlier the physical size is calculated from + * the current resolution and system DPI instead of querying the monitor EDID data. * * @thread_safety This function must only be called from the main thread. * @@ -1949,9 +2144,11 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* * * This function retrieves the content scale for the specified monitor. The * content scale is the ratio between the current DPI and the platform's - * default DPI. If you scale all pixel dimensions by this scale then your - * content should appear at an appropriate size. This is especially important - * for text and any UI elements. + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. * * The content scale may depend on both the monitor resolution and pixel * density and on user settings. It may be very different from the raw DPI @@ -2057,11 +2254,18 @@ GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); * currently set callback. This is called when a monitor is connected to or * disconnected from the system. * - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmonitorfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -2072,14 +2276,15 @@ GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); * * @ingroup monitor */ -GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback); /*! @brief Returns the available video modes for the specified monitor. * * This function returns an array of all video modes supported by the specified * monitor. The returned array is sorted in ascending order, first by color - * bit depth (the sum of all channel depths) and then by resolution area (the - * product of width and height). + * bit depth (the sum of all channel depths), then by resolution area (the + * product of width and height), then resolution width and finally by refresh + * rate. * * @param[in] monitor The monitor to query. * @param[out] count Where to store the number of video modes in the returned @@ -2137,9 +2342,9 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); /*! @brief Generates a gamma ramp and sets it for the specified monitor. * - * This function generates a 256-element gamma ramp from the specified exponent - * and then calls @ref glfwSetGammaRamp with it. The value must be a finite - * number greater than zero. + * This function generates an appropriately sized gamma ramp from the specified + * exponent and then calls @ref glfwSetGammaRamp with it. The value must be + * a finite number greater than zero. * * The software controlled gamma ramp is applied _in addition_ to the hardware * gamma correction, which today is usually an approximation of sRGB gamma. @@ -2155,7 +2360,7 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * - * @remark @wayland Gamma handling is a priviledged protocol, this function + * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. @@ -2179,7 +2384,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland Gamma handling is a priviledged protocol, this function + * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while * returning `NULL`. * @@ -2218,12 +2423,12 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark Gamma ramp sizes other than 256 are not supported by all platforms - * or graphics hardware. + * @remark The size of the specified gamma ramp should match the size of the + * current ramp for that monitor. * * @remark @win32 The gamma ramp size must be 256. * - * @remark @wayland Gamma handling is a priviledged protocol, this function + * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified gamma ramp is copied before this function @@ -2442,7 +2647,7 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * * @remark @macos When activating frame autosaving with * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified - * window size and position may be overriden by previously saved values. + * window size and position may be overridden by previously saved values. * * @remark @x11 Some window managers will not respect the placement of * initially hidden windows. @@ -2455,15 +2660,18 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * @remark @x11 The class part of the `WM_CLASS` window property will by * default be set to the window title passed to this function. The instance * part will use the contents of the `RESOURCE_NAME` environment variable, if - * present and not empty, or fall back to the window title. Set the @ref - * GLFW_X11_CLASS_NAME and @ref GLFW_X11_INSTANCE_NAME window hints to override - * this. + * present and not empty, or fall back to the window title. Set the + * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and + * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to + * override this. * - * @remark @wayland The window frame is currently very simple, only allowing - * window resize or move. A compositor can still emit close, maximize or - * fullscreen events, using for example a keybind mechanism. Additionally, - * the wp_viewporter protocol is required for this feature, otherwise the - * window will not be decorated. + * @remark @wayland Compositors should implement the xdg-decoration protocol + * for GLFW to decorate the window properly. If this protocol isn't + * supported, or if the compositor prefers client-side decorations, a very + * simple fallback frame will be drawn using the wp_viewporter protocol. A + * compositor can still emit close, maximize or fullscreen events, using for + * instance a keybind mechanism. If neither of these protocols is supported, + * the window won't be decorated. * * @remark @wayland A full screen window will not attempt to change the mode, * no matter what the requested size or refresh rate. @@ -2599,8 +2807,8 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * @param[in] images The images to create the icon from. This is ignored if * count is zero. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. @@ -2625,19 +2833,19 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); */ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); -/*! @brief Retrieves the position of the client area of the specified window. +/*! @brief Retrieves the position of the content area of the specified window. * * This function retrieves the position, in screen coordinates, of the - * upper-left corner of the client area of the specified window. + * upper-left corner of the content area of the specified window. * * Any or all of the position arguments may be `NULL`. If an error occurs, all * non-`NULL` position arguments will be set to zero. * * @param[in] window The window to query. * @param[out] xpos Where to store the x-coordinate of the upper-left corner of - * the client area, or `NULL`. + * the content area, or `NULL`. * @param[out] ypos Where to store the y-coordinate of the upper-left corner of - * the client area, or `NULL`. + * the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2657,10 +2865,10 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i */ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); -/*! @brief Sets the position of the client area of the specified window. +/*! @brief Sets the position of the content area of the specified window. * * This function sets the position, in screen coordinates, of the upper-left - * corner of the client area of the specified windowed mode window. If the + * corner of the content area of the specified windowed mode window. If the * window is a full screen window, this function does nothing. * * __Do not use this function__ to move an already visible window unless you @@ -2670,8 +2878,8 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); * cannot and should not override these limits. * * @param[in] window The window to query. - * @param[in] xpos The x-coordinate of the upper-left corner of the client area. - * @param[in] ypos The y-coordinate of the upper-left corner of the client area. + * @param[in] xpos The x-coordinate of the upper-left corner of the content area. + * @param[in] ypos The y-coordinate of the upper-left corner of the content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2692,9 +2900,9 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); */ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); -/*! @brief Retrieves the size of the client area of the specified window. +/*! @brief Retrieves the size of the content area of the specified window. * - * This function retrieves the size, in screen coordinates, of the client area + * This function retrieves the size, in screen coordinates, of the content area * of the specified window. If you wish to retrieve the size of the * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. * @@ -2703,9 +2911,9 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); * * @param[in] window The window whose size to retrieve. * @param[out] width Where to store the width, in screen coordinates, of the - * client area, or `NULL`. + * content area, or `NULL`. * @param[out] height Where to store the height, in screen coordinates, of the - * client area, or `NULL`. + * content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2724,7 +2932,7 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); /*! @brief Sets the size limits of the specified window. * - * This function sets the size limits of the client area of the specified + * This function sets the size limits of the content area of the specified * window. If the window is full screen, the size limits only take effect * once it is made windowed. If the window is not resizable, this function * does nothing. @@ -2736,14 +2944,14 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); * dimensions and all must be greater than or equal to zero. * * @param[in] window The window to set limits for. - * @param[in] minwidth The minimum width, in screen coordinates, of the client + * @param[in] minwidth The minimum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] minheight The minimum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. - * @param[in] maxwidth The maximum width, in screen coordinates, of the client + * content area, or `GLFW_DONT_CARE`. + * @param[in] maxwidth The maximum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] maxheight The maximum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. + * content area, or `GLFW_DONT_CARE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. @@ -2767,7 +2975,7 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe /*! @brief Sets the aspect ratio of the specified window. * - * This function sets the required aspect ratio of the client area of the + * This function sets the required aspect ratio of the content area of the * specified window. If the window is full screen, the aspect ratio only takes * effect once it is made windowed. If the window is not resizable, this * function does nothing. @@ -2808,9 +3016,9 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe */ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); -/*! @brief Sets the size of the client area of the specified window. +/*! @brief Sets the size of the content area of the specified window. * - * This function sets the size, in screen coordinates, of the client area of + * This function sets the size, in screen coordinates, of the content area of * the specified window. * * For full screen windows, this function updates the resolution of its desired @@ -2826,9 +3034,9 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); * * @param[in] window The window to resize. * @param[in] width The desired width, in screen coordinates, of the window - * client area. + * content area. * @param[in] height The desired height, in screen coordinates, of the window - * client area. + * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2919,9 +3127,11 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * * This function retrieves the content scale for the specified window. The * content scale is the ratio between the current DPI and the platform's - * default DPI. If you scale all pixel dimensions by this scale then your - * content should appear at an appropriate size. This is especially important - * for text and any UI elements. + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. * * On systems where each monitors can have its own content scale, the window * content scale will depend on which monitor the system considers the window @@ -3008,18 +3218,15 @@ GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); * previously restored. If the window is already iconified, this function does * nothing. * - * If the specified window is a full screen window, the original monitor - * resolution is restored until the window is restored. + * If the specified window is a full screen window, GLFW restores the original + * video mode of the monitor. The window's desired video mode is set again + * when the window is restored. * * @param[in] window The window to iconify. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland There is no concept of iconification in wl_shell, this - * function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated - * protocol. - * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify @@ -3039,8 +3246,8 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); * (minimized) or maximized. If the window is already restored, this function * does nothing. * - * If the specified window is a full screen window, the resolution chosen for - * the window is restored on the selected monitor. + * If the specified window is an iconified full screen window, its desired + * video mode is set again for its monitor when the window is restored. * * @param[in] window The window to restore. * @@ -3101,6 +3308,11 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Because Wayland wants every frame of the desktop to be + * complete, this function does not immediately make the window visible. + * Instead it will become visible the next time the window framebuffer is + * updated after this call. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide @@ -3232,7 +3444,7 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * The window position is ignored when setting a monitor. * * When the monitor is `NULL`, the position, width and height are used to - * place the window client area. The refresh rate is ignored when no monitor + * place the window content area. The refresh rate is ignored when no monitor * is specified. * * If you only wish to update the resolution of a full screen window or the @@ -3245,12 +3457,12 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * @param[in] window The window whose monitor, size or video mode to set. * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. * @param[in] xpos The desired x-coordinate of the upper-left corner of the - * client area. + * content area. * @param[in] ypos The desired y-coordinate of the upper-left corner of the - * client area. - * @param[in] width The desired with, in screen coordinates, of the client area - * or video mode. - * @param[in] height The desired height, in screen coordinates, of the client + * content area. + * @param[in] width The desired with, in screen coordinates, of the content + * area or video mode. + * @param[in] height The desired height, in screen coordinates, of the content * area or video mode. * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, * or `GLFW_DONT_CARE`. @@ -3303,6 +3515,9 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int * errors. However, this function should not fail as long as it is passed * valid arguments and the library has been [initialized](@ref intro_init). * + * @remark @wayland The Wayland protocol provides no way to check whether a + * window is iconfied, so @ref GLFW_ICONIFIED always returns `GLFW_FALSE`. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs @@ -3400,15 +3615,22 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * * This function sets the position callback of the specified window, which is * called when the window is moved. The callback is provided with the - * position, in screen coordinates, of the upper-left corner of the client area - * of the window. + * position, in screen coordinates, of the upper-left corner of the content + * area of the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int xpos, int ypos) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowposfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @wayland This callback will never be called, as there is no way for @@ -3422,20 +3644,27 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * * @ingroup window */ -GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun cbfun); +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback); /*! @brief Sets the size callback for the specified window. * * This function sets the size callback of the specified window, which is * called when the window is resized. The callback is provided with the size, - * in screen coordinates, of the client area of the window. + * in screen coordinates, of the content area of the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowsizefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3447,7 +3676,7 @@ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindow * * @ingroup window */ -GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun cbfun); +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback); /*! @brief Sets the close callback for the specified window. * @@ -3461,11 +3690,18 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * The close callback is not triggered by @ref glfwDestroyWindow. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowclosefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @macos Selecting Quit from the application menu will trigger the @@ -3480,12 +3716,12 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * * @ingroup window */ -GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun cbfun); +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback); /*! @brief Sets the refresh callback for the specified window. * * This function sets the refresh callback of the specified window, which is - * called when the client area of the window needs to be redrawn, for example + * called when the content area of the window needs to be redrawn, for example * if the window has been exposed after having been covered by another window. * * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where @@ -3493,11 +3729,18 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * very infrequently or never at all. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowrefreshfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3509,7 +3752,7 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * * @ingroup window */ -GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun cbfun); +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback); /*! @brief Sets the focus callback for the specified window. * @@ -3522,11 +3765,18 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GL * and @ref glfwSetMouseButtonCallback. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowfocusfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3537,7 +3787,7 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GL * * @ingroup window */ -GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun cbfun); +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback); /*! @brief Sets the iconify callback for the specified window. * @@ -3545,15 +3795,22 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * is called when the window is iconified or restored. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowiconifyfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @wayland The wl_shell protocol has no concept of iconification, - * this callback will never be called when using this deprecated protocol. + * @remark @wayland The XDG-shell protocol has no event for iconification, so + * this callback will never be called. * * @thread_safety This function must only be called from the main thread. * @@ -3563,7 +3820,7 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * * @ingroup window */ -GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun cbfun); +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback); /*! @brief Sets the maximize callback for the specified window. * @@ -3571,11 +3828,18 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GL * is called when the window is maximized or restored. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowmaximizefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3586,7 +3850,7 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GL * * @ingroup window */ -GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun cbfun); +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback); /*! @brief Sets the framebuffer resize callback for the specified window. * @@ -3594,11 +3858,18 @@ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, * which is called when the framebuffer of the specified window is resized. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWframebuffersizefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3609,7 +3880,7 @@ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, * * @ingroup window */ -GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun); +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback); /*! @brief Sets the window content scale callback for the specified window. * @@ -3617,11 +3888,18 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * which is called when the content scale of the specified window changes. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowcontentscalefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3633,7 +3911,7 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * * @ingroup window */ -GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun cbfun); +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback); /*! @brief Processes all pending events. * @@ -3699,10 +3977,6 @@ GLFWAPI void glfwPollEvents(void); * GLFW will pass those events on to the application callbacks before * returning. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * Event processing is not required for joystick input to work. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref @@ -3750,14 +4024,13 @@ GLFWAPI void glfwWaitEvents(void); * GLFW will pass those events on to the application callbacks before * returning. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * Event processing is not required for joystick input to work. * * @param[in] timeout The maximum amount of time, in seconds, to wait. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. @@ -3777,10 +4050,6 @@ GLFWAPI void glfwWaitEventsTimeout(double timeout); * This function posts an empty event from the current thread to the event * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * @@ -3800,11 +4069,13 @@ GLFWAPI void glfwPostEmptyEvent(void); * * This function returns the value of an input option for the specified window. * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS. + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * @param[in] window The window to query. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`. + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. @@ -3823,13 +4094,14 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * * This function sets an input mode option for the specified window. The mode * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS. + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor * modes: * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. - * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the client - * area of the window but does not restrict the cursor from leaving. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the + * content area of the window but does not restrict the cursor from leaving. * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual * and unlimited cursor movement. This is useful for implementing for * example 3D camera controls. @@ -3855,9 +4127,16 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. * + * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` + * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is + * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported, + * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref + * glfwRawMouseMotionSupported to check for support. + * * @param[in] window The window whose input mode to set. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`. + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * @param[in] value The new value of the specified input mode. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref @@ -3873,6 +4152,35 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); +/*! @brief Returns whether raw mouse motion is supported. + * + * This function returns whether raw mouse motion is supported on the current + * system. This status does not change after GLFW has been initialized so you + * only need to check this once. If you attempt to enable raw motion on + * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. + * + * Raw mouse motion is closer to the actual motion of the mouse across + * a surface. It is not affected by the scaling and acceleration applied to + * the motion of the desktop cursor. That processing is suitable for a cursor + * while raw motion is better for controlling for example a 3D camera. Because + * of this, raw mouse motion is only provided when the cursor is disabled. + * + * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref raw_mouse_motion + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwRawMouseMotionSupported(void); + /*! @brief Returns the layout-specific name of the specified printable key. * * This function returns the name of the specified printable key, encoded as @@ -3925,9 +4233,11 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark The contents of the returned string may change when a keyboard + * layout change event is received. + * * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the next call to @ref - * glfwGetKeyName, or until the library is terminated. + * should not free it yourself. It is valid until the library is terminated. * * @thread_safety This function must only be called from the main thread. * @@ -3968,8 +4278,7 @@ GLFWAPI int glfwGetKeyScancode(int key); * * This function returns the last state reported for the specified key to the * specified window. The returned state is one of `GLFW_PRESS` or - * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to - * the key callback. + * `GLFW_RELEASE`. The action `GLFW_REPEAT` is only reported to the key callback. * * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns * `GLFW_PRESS` the first time you call it for a key that was pressed, even if @@ -4011,8 +4320,8 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); * `GLFW_RELEASE`. * * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function - * `GLFW_PRESS` the first time you call it for a mouse button that was pressed, - * even if that mouse button has already been released. + * returns `GLFW_PRESS` the first time you call it for a mouse button that was + * pressed, even if that mouse button has already been released. * * @param[in] window The desired window. * @param[in] button The desired [mouse button](@ref buttons). @@ -4032,11 +4341,11 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); */ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); -/*! @brief Retrieves the position of the cursor relative to the client area of +/*! @brief Retrieves the position of the cursor relative to the content area of * the window. * * This function returns the position of the cursor, in screen coordinates, - * relative to the upper-left corner of the client area of the specified + * relative to the upper-left corner of the content area of the specified * window. * * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor @@ -4052,9 +4361,9 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); * * @param[in] window The desired window. * @param[out] xpos Where to store the cursor x-coordinate, relative to the - * left edge of the client area, or `NULL`. + * left edge of the content area, or `NULL`. * @param[out] ypos Where to store the cursor y-coordinate, relative to the to - * top edge of the client area, or `NULL`. + * top edge of the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -4070,11 +4379,11 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); */ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); -/*! @brief Sets the position of the cursor, relative to the client area of the +/*! @brief Sets the position of the cursor, relative to the content area of the * window. * * This function sets the position, in screen coordinates, of the cursor - * relative to the upper-left corner of the client area of the specified + * relative to the upper-left corner of the content area of the specified * window. The window must have input focus. If the window does not have * input focus when this function is called, it fails silently. * @@ -4089,9 +4398,9 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); * * @param[in] window The desired window. * @param[in] xpos The desired x-coordinate, relative to the left edge of the - * client area. + * content area. * @param[in] ypos The desired y-coordinate, relative to the top edge of the - * client area. + * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -4130,8 +4439,8 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * @return The handle of the created cursor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. @@ -4201,7 +4510,7 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); /*! @brief Sets the cursor for the window. * * This function sets the cursor image to be used when the cursor is over the - * client area of the specified window. The set cursor will only be visible + * content area of the specified window. The set cursor will only be visible * when the [cursor mode](@ref cursor_mode) of the window is * `GLFW_CURSOR_NORMAL`. * @@ -4250,11 +4559,18 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * scancode may be zero. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new key callback, or `NULL` to remove the currently + * @param[in] callback The new key callback, or `NULL` to remove the currently * set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWkeyfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4266,7 +4582,7 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * * @ingroup input */ -GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback); /*! @brief Sets the Unicode character callback. * @@ -4283,16 +4599,21 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * The character callback behaves as system text input normally does and will * not be called if modifier keys are held down that would prevent normal text * input on that platform, for example a Super (Command) key on macOS or Alt key - * on Windows. There is a - * [character with modifiers callback](@ref glfwSetCharModsCallback) that - * receives these events. + * on Windows. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4304,7 +4625,7 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * * @ingroup input */ -GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback); /*! @brief Sets the Unicode character with modifiers callback. * @@ -4322,11 +4643,18 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * [key callback](@ref glfwSetKeyCallback) instead. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or an * [error](@ref error_handling) occurred. * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharmodsfun). + * * @deprecated Scheduled for removal in version 4.0. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. @@ -4339,7 +4667,7 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * * @ingroup input */ -GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun); +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback); /*! @brief Sets the mouse button callback. * @@ -4353,11 +4681,18 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmousebuttonfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4369,21 +4704,28 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * * @ingroup input */ -GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun cbfun); +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback); /*! @brief Sets the cursor position callback. * * This function sets the cursor position callback of the specified window, * which is called when the cursor is moved. The callback is provided with the * position, in screen coordinates, relative to the upper-left corner of the - * client area of the window. + * content area of the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorposfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4394,20 +4736,27 @@ GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmo * * @ingroup input */ -GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun cbfun); +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback); -/*! @brief Sets the cursor enter/exit callback. +/*! @brief Sets the cursor enter/leave callback. * * This function sets the cursor boundary crossing callback of the specified - * window, which is called when the cursor enters or leaves the client area of + * window, which is called when the cursor enters or leaves the content area of * the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorenterfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4418,7 +4767,7 @@ GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursor * * @ingroup input */ -GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun cbfun); +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback); /*! @brief Sets the scroll callback. * @@ -4430,11 +4779,18 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu * wheel or a touchpad scrolling area. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new scroll callback, or `NULL` to remove the currently - * set callback. + * @param[in] callback The new scroll callback, or `NULL` to remove the + * currently set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWscrollfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4445,12 +4801,12 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu * * @ingroup input */ -GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun); +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); -/*! @brief Sets the file drop callback. +/*! @brief Sets the path drop callback. * - * This function sets the file drop callback of the specified window, which is - * called when one or more dragged files are dropped on the window. + * This function sets the path drop callback of the specified window, which is + * called when one or more dragged paths are dropped on the window. * * Because the path array and its strings may have been generated specifically * for that event, they are not guaranteed to be valid after the callback has @@ -4458,11 +4814,18 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * make a deep copy. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new file drop callback, or `NULL` to remove the + * @param[in] callback The new file drop callback, or `NULL` to remove the * currently set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWdropfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @wayland File drop is currently unimplemented. @@ -4475,7 +4838,7 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * * @ingroup input */ -GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback); /*! @brief Returns whether the specified joystick is present. * @@ -4581,7 +4944,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); * Each element in the array is one of the following values: * * Name | Value - * --------------------- | -------------------------------- + * ---- | ----- * `GLFW_HAT_CENTERED` | 0 * `GLFW_HAT_UP` | 1 * `GLFW_HAT_RIGHT` | 2 @@ -4663,7 +5026,7 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); */ GLFWAPI const char* glfwGetJoystickName(int jid); -/*! @brief Returns the SDL comaptible GUID of the specified joystick. +/*! @brief Returns the SDL compatible GUID of the specified joystick. * * This function returns the SDL compatible GUID, as a UTF-8 encoded * hexadecimal string, of the specified joystick. The returned string is @@ -4794,11 +5157,18 @@ GLFWAPI int glfwJoystickIsGamepad(int jid); * called by joystick functions. The function will then return whatever it * returns if the joystick is not present. * - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(int jid, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWjoystickfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4809,7 +5179,7 @@ GLFWAPI int glfwJoystickIsGamepad(int jid); * * @ingroup input */ -GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback); /*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. * @@ -4860,6 +5230,8 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string); * joystick is not present, does not have a mapping or an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref GLFW_INVALID_ENUM. + * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected, the gamepad mappings are updated or the library is terminated. @@ -4877,7 +5249,7 @@ GLFWAPI const char* glfwGetGamepadName(int jid); /*! @brief Retrieves the state of the specified joystick remapped as a gamepad. * - * This function retrives the state of the specified joystick remapped to + * This function retrieves the state of the specified joystick remapped to * an Xbox-like gamepad. * * If the specified joystick is not present or does not have a gamepad mapping @@ -4901,6 +5273,8 @@ GLFWAPI const char* glfwGetGamepadName(int jid); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * + * @thread_safety This function must only be called from the main thread. + * * @sa @ref gamepad * @sa @ref glfwUpdateGamepadMappings * @sa @ref glfwJoystickIsGamepad @@ -4922,8 +5296,6 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland Clipboard is currently unimplemented. - * * @pointer_lifetime The specified string is copied before this function * returns. * @@ -4949,10 +5321,8 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland Clipboard is currently unimplemented. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the next call to @ref @@ -4970,23 +5340,26 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); */ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); -/*! @brief Returns the value of the GLFW timer. +/*! @brief Returns the GLFW time. * - * This function returns the value of the GLFW timer. Unless the timer has - * been set using @ref glfwSetTime, the timer measures time elapsed since GLFW - * was initialized. + * This function returns the current GLFW time, in seconds. Unless the time + * has been set using @ref glfwSetTime it measures time elapsed since GLFW was + * initialized. + * + * This function and @ref glfwSetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. * * The resolution of the timer is system dependent, but is usually on the order * of a few micro- or nanoseconds. It uses the highest-resolution monotonic * time source on each supported platform. * - * @return The current value, in seconds, or zero if an + * @return The current time, in seconds, or zero if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Reading and - * writing of the internal timer offset is not atomic, so it needs to be + * writing of the internal base time is not atomic, so it needs to be * externally synchronized with calls to @ref glfwSetTime. * * @sa @ref time @@ -4997,23 +5370,26 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); */ GLFWAPI double glfwGetTime(void); -/*! @brief Sets the GLFW timer. +/*! @brief Sets the GLFW time. * - * This function sets the value of the GLFW timer. It then continues to count - * up from that value. The value must be a positive finite number less than - * or equal to 18446744073.0, which is approximately 584.5 years. + * This function sets the current GLFW time, in seconds. The value must be + * a positive finite number less than or equal to 18446744073.0, which is + * approximately 584.5 years. + * + * This function and @ref glfwGetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. * * @param[in] time The new value, in seconds. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_VALUE. * - * @remark The upper limit of the timer is calculated as + * @remark The upper limit of GLFW time is calculated as * floor((264 - 1) / 109) and is due to implementations * storing nanoseconds in 64 bits. The limit may be increased in the future. * * @thread_safety This function may be called from any thread. Reading and - * writing of the internal timer offset is not atomic, so it needs to be + * writing of the internal base time is not atomic, so it needs to be * externally synchronized with calls to @ref glfwGetTime. * * @sa @ref time @@ -5290,13 +5666,11 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); * This function returns whether the Vulkan loader and any minimally functional * ICD have been found. * - * The availability of a Vulkan loader and even an ICD does not by itself - * guarantee that surface creation or even instance creation is possible. - * For example, on Fermi systems Nvidia will install an ICD that provides no - * actual Vulkan support. Call @ref glfwGetRequiredInstanceExtensions to check - * whether the extensions necessary for Vulkan surface creation are available - * and @ref glfwGetPhysicalDevicePresentationSupport to check whether a queue - * family of a physical device supports image presentation. + * The availability of a Vulkan loader and even an ICD does not by itself guarantee that + * surface creation or even instance creation is possible. Call @ref + * glfwGetRequiredInstanceExtensions to check whether the extensions necessary for Vulkan + * surface creation are available and @ref glfwGetPhysicalDevicePresentationSupport to + * check whether a queue family of a physical device supports image presentation. * * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` * otherwise. @@ -5317,7 +5691,7 @@ GLFWAPI int glfwVulkanSupported(void); * * This function returns an array of names of Vulkan instance extensions required * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the - * list will always contains `VK_KHR_surface`, so if you don't require any + * list will always contain `VK_KHR_surface`, so if you don't require any * additional extensions you can pass this list directly to the * `VkInstanceCreateInfo` struct. * @@ -5342,9 +5716,6 @@ GLFWAPI int glfwVulkanSupported(void); * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. * - * @remark @macos This function currently only supports the - * `VK_MVK_macos_surface` extension from MoltenVK. - * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * library is terminated. @@ -5426,7 +5797,7 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* p * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * * @remark @macos This function currently always returns `GLFW_TRUE`, as the - * `VK_MVK_macos_surface` extension does not provide + * `VK_MVK_macos_surface` and `VK_EXT_metal_surface` extensions do not provide * a `vkGetPhysicalDevice*PresentationSupport` type function. * * @thread_safety This function may be called from any thread. For @@ -5483,8 +5854,10 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should * eliminate almost all occurrences of these errors. * - * @remark @macos This function currently only supports the - * `VK_MVK_macos_surface` extension from MoltenVK. + * @remark @macos GLFW prefers the `VK_EXT_metal_surface` extension, with the + * `VK_MVK_macos_surface` extension as a fallback. The name of the selected + * extension, if any, is included in the array returned by @ref + * glfwGetRequiredInstanceExtensions. * * @remark @macos This function creates and sets a `CAMetalLayer` instance for * the window content view, which is required for MoltenVK to function. @@ -5525,6 +5898,7 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window */ #ifndef GLAPIENTRY #define GLAPIENTRY APIENTRY + #define GLFW_GLAPIENTRY_DEFINED #endif /* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ diff --git a/examples/others/external/include/GLFW/glfw3native.h b/examples/others/external/include/GLFW/glfw3native.h index 4372cb766..7be0227d5 100644 --- a/examples/others/external/include/GLFW/glfw3native.h +++ b/examples/others/external/include/GLFW/glfw3native.h @@ -3,7 +3,7 @@ * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Löwy + * Copyright (c) 2006-2018 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -62,7 +62,6 @@ extern "C" { * * `GLFW_EXPOSE_NATIVE_COCOA` * * `GLFW_EXPOSE_NATIVE_X11` * * `GLFW_EXPOSE_NATIVE_WAYLAND` - * * `GLFW_EXPOSE_NATIVE_MIR` * * The available context API macros are: * * `GLFW_EXPOSE_NATIVE_WGL` @@ -75,6 +74,16 @@ extern "C" { * and which platform-specific headers to include. It is then up your (by * definition platform-specific) code to handle which of these should be * defined. + * + * If you do not want the platform-specific headers to be included, define + * `GLFW_NATIVE_INCLUDE_NONE` before including the @ref glfw3native.h header. + * + * @code + * #define GLFW_EXPOSE_NATIVE_WIN32 + * #define GLFW_EXPOSE_NATIVE_WGL + * #define GLFW_NATIVE_INCLUDE_NONE + * #include + * @endcode */ @@ -82,46 +91,65 @@ extern "C" { * System headers and types *************************************************************************/ -#if defined(GLFW_EXPOSE_NATIVE_WIN32) - // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for - // example to allow applications to correctly declare a GL_ARB_debug_output - // callback) but windows.h assumes no one will define APIENTRY before it does - #if defined(GLFW_APIENTRY_DEFINED) - #undef APIENTRY - #undef GLFW_APIENTRY_DEFINED - #endif - #include -#elif defined(GLFW_EXPOSE_NATIVE_COCOA) - #include - #if defined(__OBJC__) - #import - #else - typedef void* id; - #endif -#elif defined(GLFW_EXPOSE_NATIVE_X11) - #include - #include -#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) - #include -#elif defined(GLFW_EXPOSE_NATIVE_MIR) - #include -#endif +#if !defined(GLFW_NATIVE_INCLUDE_NONE) -#if defined(GLFW_EXPOSE_NATIVE_WGL) - /* WGL is declared by windows.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_NSGL) - /* NSGL is declared by Cocoa.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_GLX) - #include -#endif -#if defined(GLFW_EXPOSE_NATIVE_EGL) - #include -#endif -#if defined(GLFW_EXPOSE_NATIVE_OSMESA) - #include -#endif + #if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) + /* This is a workaround for the fact that glfw3.h needs to export APIENTRY (for + * example to allow applications to correctly declare a GL_KHR_debug callback) + * but windows.h assumes no one will define APIENTRY before it does + */ + #if defined(GLFW_APIENTRY_DEFINED) + #undef APIENTRY + #undef GLFW_APIENTRY_DEFINED + #endif + #include + #elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) + #if defined(__OBJC__) + #import + #else + #include + #include + #endif + #elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) + #include + #include + #elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) + #include + #endif + + #if defined(GLFW_EXPOSE_NATIVE_WGL) + /* WGL is declared by windows.h */ + #endif + #if defined(GLFW_EXPOSE_NATIVE_NSGL) + /* NSGL is declared by Cocoa.h */ + #endif + #if defined(GLFW_EXPOSE_NATIVE_GLX) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, glx.h will include gl.h, which will define it unconditionally + */ + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif + #include + #endif + #if defined(GLFW_EXPOSE_NATIVE_EGL) + #include + #endif + #if defined(GLFW_EXPOSE_NATIVE_OSMESA) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, osmesa.h will include gl.h, which will define it unconditionally + */ + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif + #include + #endif + +#endif /*GLFW_NATIVE_INCLUDE_NONE*/ /************************************************************************* @@ -135,6 +163,8 @@ extern "C" { * of the specified monitor, or `NULL` if an [error](@ref error_handling) * occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -150,6 +180,8 @@ GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -164,6 +196,16 @@ GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); * @return The `HWND` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -180,6 +222,17 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); * @return The `HGLRC` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -196,6 +249,8 @@ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); * @return The `CGDirectDisplayID` of the specified monitor, or * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -210,6 +265,8 @@ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); * @return The `NSWindow` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -226,6 +283,9 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); * @return The `NSOpenGLContext` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -242,6 +302,8 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); * @return The `Display` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -256,6 +318,8 @@ GLFWAPI Display* glfwGetX11Display(void); * @return The `RRCrtc` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -270,6 +334,8 @@ GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); * @return The `RROutput` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -284,6 +350,8 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); * @return The `Window` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -350,6 +418,9 @@ GLFWAPI const char* glfwGetX11SelectionString(void); * @return The `GLXContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -364,6 +435,9 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); * @return The `GLXWindow` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -380,6 +454,8 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); * @return The `struct wl_display*` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -394,6 +470,8 @@ GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); * @return The `struct wl_output*` of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -408,6 +486,8 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); * @return The main `struct wl_surface*` of the specified window, or `NULL` if * an [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -418,56 +498,17 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); #endif -#if defined(GLFW_EXPOSE_NATIVE_MIR) -/*! @brief Returns the `MirConnection*` used by GLFW. - * - * @return The `MirConnection*` used by GLFW, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirConnection* glfwGetMirDisplay(void); - -/*! @brief Returns the Mir output ID of the specified monitor. - * - * @return The Mir output ID of the specified monitor, or zero if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); - -/*! @brief Returns the `MirWindow*` of the specified window. - * - * @return The `MirWindow*` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirWindow* glfwGetMirWindow(GLFWwindow* window); -#endif - #if defined(GLFW_EXPOSE_NATIVE_EGL) /*! @brief Returns the `EGLDisplay` used by GLFW. * * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark Because EGL is initialized on demand, this function will return + * `EGL_NO_DISPLAY` until the first context has been created via EGL. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -482,6 +523,9 @@ GLFWAPI EGLDisplay glfwGetEGLDisplay(void); * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -496,6 +540,9 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -519,6 +566,9 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -540,6 +590,9 @@ GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -554,6 +607,9 @@ GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height * @return The `OSMesaContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * diff --git a/examples/others/external/lib/libglfw3.a b/examples/others/external/lib/libglfw3.a index 90e7a4759d032cb44de7e0551b3e9ed81cb9ddff..60f9e59f89276e07aac687c19f59c41103ace104 100644 GIT binary patch literal 290550 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