diff options
| author | DarkElvenAngel <[email protected]> | 2019-06-10 16:12:06 -0400 |
|---|---|---|
| committer | GitHub <[email protected]> | 2019-06-10 16:12:06 -0400 |
| commit | d7f4be071579e6f00974c0940f021272f22fbc54 (patch) | |
| tree | 6ee389e6617c494d272e9bc82415fbc3618e7a28 /src/core.c | |
| parent | 8a21830b77eaa76ffe0c31df5f96aecd6bd2eecc (diff) | |
| parent | baf7d7d19ad8d6bfbfc201169e4ed4f49a9576a6 (diff) | |
| download | raylib-d7f4be071579e6f00974c0940f021272f22fbc54.tar.gz raylib-d7f4be071579e6f00974c0940f021272f22fbc54.zip | |
Merge pull request #1 from raysan5/master
Update
Diffstat (limited to 'src/core.c')
| -rw-r--r-- | src/core.c | 920 |
1 files changed, 671 insertions, 249 deletions
@@ -47,6 +47,14 @@ * #define SUPPORT_MOUSE_GESTURES * Mouse gestures are directly mapped like touches and processed by gestures system. * +* #define SUPPORT_TOUCH_AS_MOUSE +* Touch input and mouse input are shared. Mouse functions also return touch information. +* +* #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 or +* blocking the device is 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 * @@ -59,6 +67,10 @@ * #define SUPPORT_GIF_RECORDING * Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() * +* #define SUPPORT_HIGH_DPI +* Allow scale all the drawn content to match the high-DPI equivalent size (only PLATFORM_DESKTOP) +* NOTE: This flag is forced on macOS, since most displays are high-DPI +* * DEPENDENCIES: * rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly) * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) @@ -68,7 +80,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2019 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. @@ -92,6 +104,8 @@ // Check if config flags have been externally provided on compilation line #if !defined(EXTERNAL_CONFIG_FLAGS) #include "config.h" // Defines module configuration flags +#else + #define RAYLIB_VERSION "2.5" #endif #if (defined(__linux__) || defined(PLATFORM_WEB)) && _POSIX_C_SOURCE < 199309L @@ -122,6 +136,10 @@ #include "external/rgif.h" // Support GIF recording #endif +#if defined(__APPLE__) + #define SUPPORT_HIGH_DPI // Force HighDPI support on macOS +#endif + #include <stdio.h> // Standard input / output lib #include <stdlib.h> // Required for: malloc(), free(), rand(), atexit() #include <stdint.h> // Required for: typedef unsigned long long int uint64_t, used by hi-res timer @@ -132,7 +150,7 @@ #include <ctype.h> // Required for: tolower() [Used in IsFileExtension()] #include <sys/stat.h> // Required for stat() [Used in GetLastWriteTime()] -#if defined(PLATFORM_DESKTOP) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__)) +#if (defined(PLATFORM_DESKTOP) || defined(PLATFORM_UWP)) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__)) #include "external/dirent.h" // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()] #else #include <dirent.h> // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()] @@ -178,15 +196,14 @@ //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition #include <GLFW/glfw3native.h> // Required for: glfwGetCocoaWindow() - #endif #endif #if defined(__linux__) - #include <linux/limits.h> // for NAME_MAX and PATH_MAX defines - #define MAX_FILEPATH_LENGTH PATH_MAX // Use Linux define (4096) + #include <linux/limits.h> // for NAME_MAX and PATH_MAX defines + #define MAX_FILEPATH_LENGTH PATH_MAX // Use Linux define (4096) #else - #define MAX_FILEPATH_LENGTH 256 // Use common value + #define MAX_FILEPATH_LENGTH 512 // Use common value #endif #if defined(PLATFORM_ANDROID) @@ -273,6 +290,7 @@ static GLFWwindow *window; // Native window (graphic device #endif static bool windowReady = false; // Check if window has been initialized successfully static bool windowMinimized = false; // Check if window has been minimized +static bool windowResized = false; // Check if window has been resized static const char *windowTitle = NULL; // Window text title... static unsigned int displayWidth, displayHeight;// Display width and height (monitor, device-screen, LCD, ...) @@ -282,7 +300,7 @@ static int currentWidth, currentHeight; // Current render width and heig static int renderOffsetX = 0; // Offset X from render area (must be divided by 2) static int renderOffsetY = 0; // Offset Y from render area (must be divided by 2) static bool fullscreen = false; // Fullscreen mode (useful only for PLATFORM_DESKTOP) -static Matrix downscaleView; // Matrix to downscale view (in case screen size bigger than display size) +static Matrix screenScaling; // Matrix to scale screen (framebuffer rendering) #if defined(PLATFORM_RPI) static EGL_DISPMANX_WINDOW_T nativeWindow; // Native window (graphic device) @@ -359,6 +377,15 @@ typedef struct { static InputEventWorker eventWorkers[10]; // List of worker threads for every monitored "/dev/input/event<N>" +typedef struct{ + int Contents[8]; + char Head; + char Tail; +} KeyEventFifo; + +static KeyEventFifo lastKeyPressedEvdev; // Buffer for holding keydown events as they arrive (Needed due to multitreading of event workers) +static char currentKeyStateEvdev[512] = { 0 }; // Registers current frame key state from event based driver (Needs to be seperate because the legacy console based method clears keys on every frame) + #endif #if defined(PLATFORM_WEB) static bool toggleCursorLock = false; // Ask for cursor pointer lock on next click @@ -426,7 +453,7 @@ extern void UnloadDefaultFont(void); // [Module: text] Unloads default fo //---------------------------------------------------------------------------------- static bool InitGraphicsDevice(int width, int height); // Initialize graphics device static void SetupFramebuffer(int width, int height); // Setup main framebuffer -static void SetupViewport(void); // Set viewport parameters +static void SetupViewport(int width, int height); // Set viewport for a provided width and height static void SwapBuffers(void); // Copy back buffer to front buffers static void InitTimer(void); // Initialize timer @@ -434,6 +461,8 @@ static void Wait(float ms); // Wait for some millise static bool GetKeyStatus(int key); // Returns if a key has been pressed static bool GetMouseButtonStatus(int button); // Returns if a mouse button has been pressed +static int GetGamepadButton(int button); // Get gamepad button generic to all platforms +static int GetGamepadAxis(int axis); // Get gamepad axis generic to all platforms static void PollInputEvents(void); // Register user events static void LogoAnimation(void); // Plays raylib logo appearing animation @@ -465,19 +494,19 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE #endif #if defined(PLATFORM_RPI) +#if defined(SUPPORT_SSH_KEYBOARD_RPI) static void InitKeyboard(void); // Init raw keyboard system (standard input reading) static void ProcessKeyboard(void); // Process keyboard events static void RestoreKeyboard(void); // Restore keyboard system -static void InitMouse(void); // Mouse initialization (including mouse thread) +#endif + +static void InitEvdevInput(void); // Evdev inputs initialization static void EventThreadSpawn(char *device); // Identifies a input device and spawns a thread to handle it if needed static void *EventThread(void *arg); // Input device events reading thread + static void InitGamepad(void); // Init raw gamepad input static void *GamepadThread(void *arg); // Mouse reading thread -#endif - -#if defined(PLATFORM_UWP) -// TODO: Define functions required to manage inputs -#endif +#endif // PLATFORM_RPI #if defined(_WIN32) // NOTE: We include Sleep() function signature here to avoid windows.h inclusion @@ -588,9 +617,11 @@ void InitWindow(int width, int height, const char *title) #if defined(PLATFORM_RPI) // Init raw input system - InitMouse(); // Mouse init - InitKeyboard(); // Keyboard init + InitEvdevInput(); // Evdev inputs initialization InitGamepad(); // Gamepad init +#if defined(SUPPORT_SSH_KEYBOARD_RPI) + InitKeyboard(); // Keyboard init +#endif #endif #if defined(PLATFORM_WEB) @@ -607,8 +638,6 @@ void InitWindow(int width, int height, const char *title) emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - //emscripten_set_touchstart_callback(0, NULL, 1, Emscripten_HandleTouch); - //emscripten_set_touchend_callback("#canvas", data, 0, Emscripten_HandleTouch); // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); @@ -624,7 +653,7 @@ void InitWindow(int width, int height, const char *title) SetTargetFPS(60); LogoAnimation(); } -#endif // defined(PLATFORM_ANDROID) +#endif // PLATFORM_ANDROID } // Close window and unload OpenGL context @@ -707,7 +736,7 @@ bool WindowShouldClose(void) { #if defined(PLATFORM_WEB) // Emterpreter-Async required to run sync code - // https://github.com/kripken/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-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! @@ -742,6 +771,16 @@ bool IsWindowMinimized(void) #endif } +// Check if window has been resized +bool IsWindowResized(void) +{ +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP) + return windowResized; +#else + return false; +#endif +} + // Check if window is currently hidden bool IsWindowHidden(void) { @@ -768,30 +807,30 @@ void ToggleFullscreen(void) } // Set icon for window (only PLATFORM_DESKTOP) +// NOTE: Image must be in RGBA format, 8bit per channel void SetWindowIcon(Image image) { #if defined(PLATFORM_DESKTOP) - Image imicon = ImageCopy(image); - ImageFormat(&imicon, UNCOMPRESSED_R8G8B8A8); - - GLFWimage icon[1] = { 0 }; - - icon[0].width = imicon.width; - icon[0].height = imicon.height; - icon[0].pixels = (unsigned char *)imicon.data; + if (image.format == UNCOMPRESSED_R8G8B8A8) + { + GLFWimage icon[1] = { 0 }; - // NOTE 1: We only support one image icon - // NOTE 2: The specified image data is copied before this function returns - glfwSetWindowIcon(window, 1, icon); + icon[0].width = image.width; + icon[0].height = image.height; + icon[0].pixels = (unsigned char *)image.data; - // TODO: Support multi-image icons --> image.mipmaps - UnloadImage(imicon); + // NOTE 1: We only support one image icon + // NOTE 2: The specified image data is copied before this function returns + glfwSetWindowIcon(window, 1, icon); + } + else TraceLog(LOG_WARNING, "Window icon image must be in R8G8B8A8 pixel format"); #endif } // Set title for window (only PLATFORM_DESKTOP) void SetWindowTitle(const char *title) { + windowTitle = title; #if defined(PLATFORM_DESKTOP) glfwSetWindowTitle(window, title); #endif @@ -831,6 +870,7 @@ void SetWindowMinSize(int width, int height) } // Set window dimensions +// TODO: Issues on HighDPI scaling void SetWindowSize(int width, int height) { #if defined(PLATFORM_DESKTOP) @@ -869,9 +909,9 @@ int GetScreenHeight(void) // Get native window handle void *GetWindowHandle(void) { -#if defined(_WIN32) +#if defined(PLATFORM_DESKTOP) && defined(_WIN32) // NOTE: Returned handle is: void *HWND (windows.h) - return glfwGetWin32Window(window); + return glfwGetWin32Window(window); #elif defined(__linux__) // NOTE: Returned handle is: unsigned long Window (X.h) // typedef unsigned long XID; @@ -990,6 +1030,8 @@ const char *GetClipboardText(void) { #if defined(PLATFORM_DESKTOP) return glfwGetClipboardString(window); +#else + return NULL; #endif } @@ -1007,6 +1049,11 @@ void ShowCursor(void) #if defined(PLATFORM_DESKTOP) glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); #endif +#if defined(PLATFORM_UWP) + UWPMessage *msg = CreateUWPMessage(); + msg->type = UWP_MSG_SHOW_MOUSE; + SendMessageToUWP(msg); +#endif cursorHidden = false; } @@ -1016,6 +1063,11 @@ void HideCursor(void) #if defined(PLATFORM_DESKTOP) glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); #endif +#if defined(PLATFORM_UWP) + UWPMessage *msg = CreateUWPMessage(); + msg->type = UWP_MSG_HIDE_MOUSE; + SendMessageToUWP(msg); +#endif cursorHidden = true; } @@ -1034,6 +1086,11 @@ void EnableCursor(void) #if defined(PLATFORM_WEB) toggleCursorLock = true; #endif +#if defined(PLATFORM_UWP) + UWPMessage *msg = CreateUWPMessage(); + msg->type = UWP_MSG_LOCK_MOUSE; + SendMessageToUWP(msg); +#endif cursorHidden = false; } @@ -1046,6 +1103,11 @@ void DisableCursor(void) #if defined(PLATFORM_WEB) toggleCursorLock = true; #endif +#if defined(PLATFORM_UWP) + UWPMessage *msg = CreateUWPMessage(); + msg->type = UWP_MSG_UNLOCK_MOUSE; + SendMessageToUWP(msg); +#endif cursorHidden = true; } @@ -1064,7 +1126,7 @@ void BeginDrawing(void) previousTime = currentTime; rlLoadIdentity(); // Reset current matrix (MODELVIEW) - rlMultMatrixf(MatrixToFloat(downscaleView)); // If downscale required, apply it here + rlMultMatrixf(MatrixToFloat(screenScaling)); // Apply screen scaling //rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1 // NOTE: Not required with OpenGL 3.3+ @@ -1091,7 +1153,7 @@ void EndDrawing(void) unsigned char *screenData = rlReadScreenPixels(screenWidth, screenHeight); GifWriteFrame(screenData, screenWidth, screenHeight, 10, 8, false); - free(screenData); // Free image data + RL_FREE(screenData); // Free image data } if (((gifFramesCounter/15)%2) == 1) @@ -1125,6 +1187,8 @@ void EndDrawing(void) frameTime += extraTime; } + + return; } // Initialize 2D mode with custom camera (2D) @@ -1133,6 +1197,7 @@ void BeginMode2D(Camera2D camera) rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlLoadIdentity(); // Reset current matrix (MODELVIEW) + rlMultMatrixf(MatrixToFloat(screenScaling)); // Apply screen scaling if required // Camera rotation and scaling is always relative to target Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f); @@ -1142,7 +1207,7 @@ void BeginMode2D(Camera2D camera) Matrix matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation); - rlMultMatrixf(MatrixToFloat(matTransform)); + rlMultMatrixf(MatrixToFloat(matTransform)); // Apply transformation to modelview } // Ends 2D mode with custom camera @@ -1151,6 +1216,7 @@ void EndMode2D(void) rlglDraw(); // Draw Buffers (Only OpenGL 3+ and ES2) rlLoadIdentity(); // Reset current matrix (MODELVIEW) + rlMultMatrixf(MatrixToFloat(screenScaling)); // Apply screen scaling if required } // Initializes 3D mode with custom camera (3D) @@ -1204,6 +1270,8 @@ void EndMode3D(void) rlMatrixMode(RL_MODELVIEW); // Get back to modelview matrix rlLoadIdentity(); // Reset current matrix (MODELVIEW) + rlMultMatrixf(MatrixToFloat(screenScaling)); // Apply screen scaling if required + rlDisableDepthTest(); // Disable DEPTH_TEST for 2D } @@ -1228,7 +1296,7 @@ void BeginTextureMode(RenderTexture2D target) rlLoadIdentity(); // Reset current matrix (MODELVIEW) //rlScalef(0.0f, -1.0f, 0.0f); // Flip Y-drawing (?) - + // Setup current width/height for proper aspect ratio // calculation when using BeginMode3D() currentWidth = target.texture.width; @@ -1242,19 +1310,9 @@ void EndTextureMode(void) rlDisableRenderTexture(); // Disable render target - // Set viewport to default framebuffer size (screen size) - SetupViewport(); - - rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix - rlLoadIdentity(); // Reset current matrix (PROJECTION) - - // Set orthographic projection to current framebuffer size - // NOTE: Configured top-left corner as (0, 0) - rlOrtho(0, GetScreenWidth(), GetScreenHeight(), 0, 0.0f, 1.0f); + // Set viewport to default framebuffer size + SetupViewport(renderWidth, renderHeight); - rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix - rlLoadIdentity(); // Reset current matrix (MODELVIEW) - // Reset current screen size currentWidth = GetScreenWidth(); currentHeight = GetScreenHeight(); @@ -1401,6 +1459,11 @@ double GetTime(void) return (double)(time - baseTime)*1e-9; // Elapsed time since InitTimer() #endif + +#if defined(PLATFORM_UWP) + // Updated through messages + return currentTime; +#endif } // Returns hexadecimal value for a Color @@ -1430,11 +1493,11 @@ Vector3 ColorToHSV(Color color) Vector3 hsv = { 0.0f, 0.0f, 0.0f }; float min, max, delta; - min = rgb.x < rgb.y ? rgb.x : rgb.y; - min = min < rgb.z ? min : rgb.z; + min = rgb.x < rgb.y? rgb.x : rgb.y; + min = min < rgb.z? min : rgb.z; - max = rgb.x > rgb.y ? rgb.x : rgb.y; - max = max > rgb.z ? max : rgb.z; + max = rgb.x > rgb.y? rgb.x : rgb.y; + max = max > rgb.z? max : rgb.z; hsv.z = max; // Value delta = max - min; @@ -1485,25 +1548,25 @@ Color ColorFromHSV(Vector3 hsv) // Red channel float k = fmod((5.0f + h/60.0f), 6); float t = 4.0f - k; - k = (t < k) ? t : k; - k = (k < 1) ? k : 1; - k = (k > 0) ? k : 0; + k = (t < k)? t : k; + k = (k < 1)? k : 1; + k = (k > 0)? k : 0; color.r = (v - v*s*k)*255; // Green channel k = fmod((3.0f + h/60.0f), 6); t = 4.0f - k; - k = (t < k) ? t : k; - k = (k < 1) ? k : 1; - k = (k > 0) ? k : 0; + k = (t < k)? t : k; + k = (k < 1)? k : 1; + k = (k > 0)? k : 0; color.g = (v - v*s*k)*255; // Blue channel k = fmod((1.0f + h/60.0f), 6); t = 4.0f - k; - k = (t < k) ? t : k; - k = (k < 1) ? k : 1; - k = (k > 0) ? k : 0; + k = (t < k)? t : k; + k = (k < 1)? k : 1; + k = (k > 0)? k : 0; color.b = (v - v*s*k)*255; return color; @@ -1573,12 +1636,12 @@ void TakeScreenshot(const char *fileName) #endif ExportImage(image, path); - free(imgData); + RL_FREE(imgData); #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')", GetFileName(path), GetFileName(path))); + // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html + emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); #endif TraceLog(LOG_INFO, "Screenshot taken: %s", path); @@ -1661,37 +1724,26 @@ const char *GetFileName(const char *filePath) // Get filename string without extension (memory should be freed) const char *GetFileNameWithoutExt(const char *filePath) { - char *result, *lastDot, *lastSep; - - char nameDot = '.'; // Default filename to extension separator character - char pathSep = '/'; // Default filepath separator character - - // Error checks and allocate string - if (filePath == NULL) return NULL; + #define MAX_FILENAMEWITHOUTEXT_LENGTH 64 - // Try to allocate new string, same size as original - // NOTE: By default strlen() does not count the '\0' character - if ((result = (char *)malloc(strlen(filePath) + 1)) == NULL) return NULL; + static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH]; + memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH); - strcpy(result, filePath); // Make a copy of the string + strcpy(fileName, GetFileName(filePath)); // Get filename with extension - // NOTE: strrchr() returns a pointer to the last occurrence of character - lastDot = strrchr(result, nameDot); - lastSep = (pathSep == 0) ? NULL : strrchr(result, pathSep); + int len = strlen(fileName); - if (lastDot != NULL) // Check if it has an extension separator... + for (int i = 0; (i < len) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++) { - if (lastSep != NULL) // ...and it's before the extenstion separator... + if (fileName[i] == '.') { - if (lastSep < lastDot) - { - *lastDot = '\0'; // ...then remove it - } + // NOTE: We break on first '.' found + fileName[i] = '\0'; + break; } - else *lastDot = '\0'; // Has extension separator with no path separator } - return result; // Return the modified string + return fileName; } // Get directory for a given fileName (with path) @@ -1731,8 +1783,8 @@ char **GetDirectoryFiles(const char *dirPath, int *fileCount) ClearDirectoryFiles(); // Memory allocation for MAX_DIRECTORY_FILES - dirFilesPath = (char **)malloc(sizeof(char *)*MAX_DIRECTORY_FILES); - for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH); + dirFilesPath = (char **)RL_MALLOC(sizeof(char *)*MAX_DIRECTORY_FILES); + for (int i = 0; i < MAX_DIRECTORY_FILES; i++) dirFilesPath[i] = (char *)RL_MALLOC(sizeof(char)*MAX_FILEPATH_LENGTH); int counter = 0; struct dirent *ent; @@ -1765,9 +1817,9 @@ void ClearDirectoryFiles(void) { if (dirFilesCount > 0) { - for (int i = 0; i < dirFilesCount; i++) free(dirFilesPath[i]); + for (int i = 0; i < dirFilesCount; i++) RL_FREE(dirFilesPath[i]); - free(dirFilesPath); + RL_FREE(dirFilesPath); dirFilesCount = 0; } } @@ -1797,9 +1849,9 @@ void ClearDroppedFiles(void) { if (dropFilesCount > 0) { - for (int i = 0; i < dropFilesCount; i++) free(dropFilesPath[i]); + for (int i = 0; i < dropFilesCount; i++) RL_FREE(dropFilesPath[i]); - free(dropFilesPath); + RL_FREE(dropFilesPath); dropFilesCount = 0; } @@ -1914,18 +1966,17 @@ void OpenURL(const char *url) } else { - char *cmd = (char *)calloc(strlen(url) + 10, sizeof(char)); + char *cmd = (char *)RL_CALLOC(strlen(url) + 10, sizeof(char)); #if defined(_WIN32) - sprintf(cmd, "explorer '%s'", url); + sprintf(cmd, "explorer %s", url); #elif defined(__linux__) sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser #elif defined(__APPLE__) sprintf(cmd, "open '%s'", url); #endif system(cmd); - - free(cmd); + RL_FREE(cmd); } } @@ -2122,6 +2173,12 @@ bool IsMouseButtonPressed(int button) if ((currentMouseState[button] != previousMouseState[button]) && (currentMouseState[button] == 1)) pressed = true; #endif +/* +#if defined(PLATFORM_WEB) + Vector2 pos = GetTouchPosition(0); + if ((pos.x > 0) && (pos.y > 0)) pressed = true; // There was a touch! +#endif +*/ return pressed; } @@ -2186,11 +2243,22 @@ int GetMouseY(void) // Returns mouse position XY Vector2 GetMousePosition(void) { + Vector2 position = { 0.0f, 0.0f }; + #if defined(PLATFORM_ANDROID) - return GetTouchPosition(0); + position = GetTouchPosition(0); #else - return (Vector2){ (mousePosition.x + mouseOffset.x)*mouseScale.x, (mousePosition.y + mouseOffset.y)*mouseScale.y }; + position = (Vector2){ (mousePosition.x + mouseOffset.x)*mouseScale.x, (mousePosition.y + mouseOffset.y)*mouseScale.y }; #endif +/* +#if defined(PLATFORM_WEB) + Vector2 pos = GetTouchPosition(0); + + // Touch position has priority over mouse position + if ((pos.x > 0) && (pos.y > 0)) position = pos; // There was a touch! +#endif +*/ + return position; } // Set mouse position XY @@ -2201,6 +2269,13 @@ void SetMousePosition(int x, int y) // NOTE: emscripten not implemented glfwSetCursorPos(window, mousePosition.x, mousePosition.y); #endif +#if defined(PLATFORM_UWP) + UWPMessage *msg = CreateUWPMessage(); + msg->type = UWP_MSG_SET_MOUSE_LOCATION; + msg->paramVector0.x = mousePosition.x; + msg->paramVector0.y = mousePosition.y; + SendMessageToUWP(msg); +#endif } // Set mouse offset @@ -2214,7 +2289,7 @@ void SetMouseOffset(int offsetX, int offsetY) // NOTE: Useful when rendering to different size targets void SetMouseScale(float scaleX, float scaleY) { - mouseScale = (Vector2){ scaleX, scaleY }; + mouseScale = (Vector2){ scaleX, scaleY }; } // Returns mouse wheel movement Y @@ -2294,12 +2369,11 @@ static bool InitGraphicsDevice(int width, int height) currentWidth = width; currentHeight = height; + screenScaling = MatrixIdentity(); // No draw scaling required by default + // NOTE: Framebuffer (render area - renderWidth, renderHeight) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scalings) - // Downscale matrix is required in case desired screen area is bigger than display area - downscaleView = MatrixIdentity(); - #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) glfwSetErrorCallback(ErrorCallback); @@ -2330,14 +2404,14 @@ static bool InitGraphicsDevice(int width, int height) // Screen size security check if (screenWidth <= 0) screenWidth = displayWidth; if (screenHeight <= 0) screenHeight = displayHeight; -#endif // defined(PLATFORM_DESKTOP) +#endif // PLATFORM_DESKTOP #if defined(PLATFORM_WEB) displayWidth = screenWidth; displayHeight = screenHeight; -#endif // defined(PLATFORM_WEB) +#endif // PLATFORM_WEB - glfwDefaultWindowHints(); // Set default windows hints: + 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 @@ -2346,6 +2420,10 @@ static bool InitGraphicsDevice(int width, int height) //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 +#if defined(PLATFORM_DESKTOP) && defined(SUPPORT_HIGH_DPI) + // NOTE: If using external GLFW, it requires latest GLFW 3.3 for this functionality + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on +#endif // Check some Window creation flags if (configFlags & FLAG_WINDOW_HIDDEN) glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // Visible window @@ -2392,7 +2470,11 @@ static bool InitGraphicsDevice(int width, int height) 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); // Alternative: GLFW_EGL_CONTEXT_API (ANGLE) +#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 (fullscreen) @@ -2422,9 +2504,11 @@ static bool InitGraphicsDevice(int width, int height) // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched // by the sides to fit all monitor space... - // At this point we need to manage render size vs screen size - // NOTE: This function uses and modifies global module variables: - // screenWidth/screenHeight - renderWidth/renderHeight - downscaleView + // Try to setup the most appropiate 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: screenWidth/screenHeight - renderWidth/renderHeight - renderOffsetX/renderOffsetY - screenScaling + // 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(displayWidth, displayHeight); window = glfwCreateWindow(displayWidth, displayHeight, windowTitle, glfwGetPrimaryMonitor(), NULL); @@ -2503,7 +2587,7 @@ static bool InitGraphicsDevice(int width, int height) glfwSwapInterval(1); TraceLog(LOG_INFO, "Trying to enable VSYNC"); } -#endif // defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) +#endif // PLATFORM_DESKTOP || PLATFORM_WEB #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP) fullscreen = true; @@ -2660,8 +2744,6 @@ static bool InitGraphicsDevice(int width, int height) } } - //SetupFramebuffer(displayWidth, displayHeight); - EGLint numConfigs = 0; if ((eglChooseConfig(display, framebufferAttribs, &config, 1, &numConfigs) == EGL_FALSE) || (numConfigs == 0)) { @@ -2765,20 +2847,20 @@ static bool InitGraphicsDevice(int width, int height) eglGetConfigAttrib(display, 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: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView + // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and screenScaling SetupFramebuffer(displayWidth, displayHeight); ANativeWindow_setBuffersGeometry(androidApp->window, renderWidth, renderHeight, displayFormat); //ANativeWindow_setBuffersGeometry(androidApp->window, 0, 0, displayFormat); // Force use of native display size surface = eglCreateWindowSurface(display, config, androidApp->window, NULL); -#endif // defined(PLATFORM_ANDROID) +#endif // PLATFORM_ANDROID #if defined(PLATFORM_RPI) graphics_get_display_size(0, &displayWidth, &displayHeight); // At this point we need to manage render size vs screen size - // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and downscaleView + // NOTE: This function use and modify global module variables: screenWidth/screenHeight and renderWidth/renderHeight and screenScaling SetupFramebuffer(displayWidth, displayHeight); dstRect.x = 0; @@ -2812,7 +2894,8 @@ static bool InitGraphicsDevice(int width, int height) surface = eglCreateWindowSurface(display, config, &nativeWindow, NULL); //--------------------------------------------------------------------------------- -#endif // defined(PLATFORM_RPI) +#endif // PLATFORM_RPI + // There must be at least one frame displayed before the buffers are swapped //eglSwapInterval(display, 1); @@ -2833,25 +2916,25 @@ static bool InitGraphicsDevice(int width, int height) TraceLog(LOG_INFO, "Screen size: %i x %i", screenWidth, screenHeight); TraceLog(LOG_INFO, "Viewport offsets: %i, %i", renderOffsetX, renderOffsetY); } -#endif // defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) - - renderWidth = screenWidth; - renderHeight = screenHeight; +#endif // PLATFORM_ANDROID || PLATFORM_RPI // Initialize OpenGL context (states and resources) - // NOTE: screenWidth and screenHeight not used, just stored as globals + // NOTE: screenWidth and screenHeight not used, just stored as globals in rlgl rlglInit(screenWidth, screenHeight); - // Setup default viewport - SetupViewport(); + int fbWidth = renderWidth; + int fbHeight = renderHeight; - // Initialize internal projection and modelview matrices - // NOTE: Default to orthographic projection mode with top-left corner at (0,0) - rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix - rlLoadIdentity(); // Reset current matrix (PROJECTION) - rlOrtho(0, renderWidth - renderOffsetX, renderHeight - renderOffsetY, 0, 0.0f, 1.0f); - rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix - rlLoadIdentity(); // Reset current matrix (MODELVIEW) +#if defined(PLATFORM_DESKTOP) && defined(SUPPORT_HIGH_DPI) + glfwGetFramebufferSize(window, &fbWidth, &fbHeight); + + // Screen scaling matrix is required in case desired screen area is different than display area + screenScaling = MatrixScale((float)fbWidth/screenWidth, (float)fbHeight/screenHeight, 1.0f); + SetMouseScale((float)screenWidth/fbWidth, (float)screenHeight/fbHeight); +#endif // PLATFORM_DESKTOP && SUPPORT_HIGH_DPI + + // Setup default viewport + SetupViewport(fbWidth, fbHeight); ClearBackground(RAYWHITE); // Default background color for raylib games :P @@ -2861,22 +2944,26 @@ static bool InitGraphicsDevice(int width, int height) return true; } -// Set viewport parameters -static void SetupViewport(void) +// Set viewport for a provided width and height +static void SetupViewport(int width, int height) { -#if defined(__APPLE__) - // Get framebuffer size of current window - // NOTE: Required to handle HighDPI display correctly on OSX because framebuffer - // is automatically reasized to adapt to new DPI. - // When OS does that, it can be detected using GLFW3 callback: glfwSetFramebufferSizeCallback() - int fbWidth, fbHeight; - glfwGetFramebufferSize(window, &fbWidth, &fbHeight); - rlViewport(renderOffsetX/2, renderOffsetY/2, fbWidth - renderOffsetX, fbHeight - renderOffsetY); -#else - // Initialize screen viewport (area of the screen that you will actually draw to) - // NOTE: Viewport must be recalculated if screen is resized + renderWidth = width; + renderHeight = height; + + // Set viewport width and height + // NOTE: We consider render size 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) rlViewport(renderOffsetX/2, renderOffsetY/2, renderWidth - renderOffsetX, renderHeight - renderOffsetY); -#endif + + rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix + rlLoadIdentity(); // Reset current matrix (PROJECTION) + + // Set orthographic projection to current framebuffer size + // NOTE: Configured top-left corner as (0, 0) + rlOrtho(0, renderWidth, renderHeight, 0, 0.0f, 1.0f); + + rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix + rlLoadIdentity(); // Reset current matrix (MODELVIEW) } // Compute framebuffer size relative to screen size and display size @@ -2907,10 +2994,9 @@ static void SetupFramebuffer(int width, int height) renderOffsetY = 0; } - // NOTE: downscale matrix required! + // Screen scaling required float scaleRatio = (float)renderWidth/(float)screenWidth; - - downscaleView = MatrixScale(scaleRatio, scaleRatio, scaleRatio); + screenScaling = MatrixScale(scaleRatio, scaleRatio, 1.0f); // NOTE: We render to full display resolution! // We just need to calculate above parameters for downscale matrix and offsets @@ -2978,9 +3064,10 @@ static void InitTimer(void) // 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 // http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected +// http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timming on Win32! static void Wait(float ms) { -#if defined(SUPPORT_BUSY_WAIT_LOOP) +#if defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_UWP) double prevTime = GetTime(); double nextTime = 0.0; @@ -3013,7 +3100,7 @@ static bool GetKeyStatus(int key) // NOTE: Android supports up to 260 keys if (key < 0 || key > 260) return false; else return currentKeyState[key]; -#elif defined(PLATFORM_RPI) +#elif defined(PLATFORM_RPI) || defined(PLATFORM_UWP) // NOTE: Keys states are filled in PollInputEvents() if (key < 0 || key > 511) return false; else return currentKeyState[key]; @@ -3028,12 +3115,105 @@ static bool GetMouseButtonStatus(int button) #elif defined(PLATFORM_ANDROID) // TODO: Check for virtual mouse? return false; -#elif defined(PLATFORM_RPI) +#elif defined(PLATFORM_RPI) || defined(PLATFORM_UWP) // NOTE: Mouse buttons states are filled in PollInputEvents() return currentMouseState[button]; #endif } +// Get gamepad button generic to all platforms +static int GetGamepadButton(int button) +{ + int btn = GAMEPAD_BUTTON_UNKNOWN; +#if defined(PLATFORM_DESKTOP) + switch (button) + { + case GLFW_GAMEPAD_BUTTON_Y: btn = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_B: btn = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_A: btn = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_X: btn = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + + case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: btn = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: btn = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + + case GLFW_GAMEPAD_BUTTON_BACK: btn = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_GUIDE: btn = GAMEPAD_BUTTON_MIDDLE; break; + case GLFW_GAMEPAD_BUTTON_START: btn = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + + case GLFW_GAMEPAD_BUTTON_DPAD_UP: btn = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: btn = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: btn = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: btn = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + + case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: btn = GAMEPAD_BUTTON_LEFT_THUMB; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: btn = GAMEPAD_BUTTON_RIGHT_THUMB; break; + } +#endif + +#if defined(PLATFORM_UWP) + btn = button; // UWP will provide the correct button +#endif + +#if defined(PLATFORM_WEB) + // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface + switch (button) + { + case 0: btn = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case 1: btn = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case 2: btn = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case 3: btn = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case 4: btn = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case 5: btn = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + case 6: btn = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; + case 7: btn = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; + case 8: btn = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case 9: btn = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + case 10: btn = GAMEPAD_BUTTON_LEFT_THUMB; break; + case 11: btn = GAMEPAD_BUTTON_RIGHT_THUMB; break; + case 12: btn = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case 13: btn = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case 14: btn = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case 15: btn = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + } +#endif + + return btn; +} + +// Get gamepad axis generic to all platforms +static int GetGamepadAxis(int axis) +{ + int axs = GAMEPAD_AXIS_UNKNOWN; +#if defined(PLATFORM_DESKTOP) + switch (axis) + { + case GLFW_GAMEPAD_AXIS_LEFT_X: axs = GAMEPAD_AXIS_LEFT_X; break; + case GLFW_GAMEPAD_AXIS_LEFT_Y: axs = GAMEPAD_AXIS_LEFT_Y; break; + case GLFW_GAMEPAD_AXIS_RIGHT_X: axs = GAMEPAD_AXIS_RIGHT_X; break; + case GLFW_GAMEPAD_AXIS_RIGHT_Y: axs = GAMEPAD_AXIS_RIGHT_Y; break; + case GLFW_GAMEPAD_AXIS_LEFT_TRIGGER: axs = GAMEPAD_AXIS_LEFT_TRIGGER; break; + case GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER: axs = GAMEPAD_AXIS_RIGHT_TRIGGER; break; + } +#endif + +#if defined(PLATFORM_UWP) + axs = axis; // UWP will provide the correct axis +#endif + +#if defined(PLATFORM_WEB) + // Gamepad axis reference:https://www.w3.org/TR/gamepad/#gamepad-interface + switch (axis) + { + case 0: axs = GAMEPAD_AXIS_LEFT_X; + case 1: axs = GAMEPAD_AXIS_LEFT_Y; + case 2: axs = GAMEPAD_AXIS_RIGHT_X; + case 3: axs = GAMEPAD_AXIS_RIGHT_X; + } +#endif + + return axs; +} + // Poll (store) all input events static void PollInputEvents(void) { @@ -3053,8 +3233,16 @@ static void PollInputEvents(void) #endif #if defined(PLATFORM_RPI) + // Register previous keys states - for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i]; + for (int i = 0; i < 512; i++)previousKeyState[i] = currentKeyState[i]; + + // Grab a keypress from the evdev fifo if avalable + if (lastKeyPressedEvdev.Head != lastKeyPressedEvdev.Tail) + { + lastKeyPressed = lastKeyPressedEvdev.Contents[lastKeyPressedEvdev.Tail]; // Read the key from the buffer + lastKeyPressedEvdev.Tail = (lastKeyPressedEvdev.Tail + 1) & 0x07; // Increment the tail pointer forwards and binary wraparound after 7 (fifo is 8 elements long) + } // Register previous mouse states previousMouseWheelY = currentMouseWheelY; @@ -3066,6 +3254,160 @@ static void PollInputEvents(void) } #endif +#if defined(PLATFORM_UWP) + // Register previous keys states + for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i]; + + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (gamepadReady[i]) + { + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) previousGamepadState[i][k] = currentGamepadState[i][k]; + } + } + + // Register previous mouse states + previousMouseWheelY = currentMouseWheelY; + currentMouseWheelY = 0; + for (int i = 0; i < 3; i++) previousMouseState[i] = currentMouseState[i]; + + // Loop over pending messages + while (HasMessageFromUWP()) + { + UWPMessage *msg = GetMessageFromUWP(); + + switch (msg->type) + { + case UWP_MSG_REGISTER_KEY: + { + // Convert from virtualKey + int actualKey = -1; + + switch (msg->paramInt0) + { + case 0x08: actualKey = KEY_BACKSPACE; break; + case 0x20: actualKey = KEY_SPACE; break; + case 0x1B: actualKey = KEY_ESCAPE; break; + case 0x0D: actualKey = KEY_ENTER; break; + case 0x2E: actualKey = KEY_DELETE; break; + case 0x27: actualKey = KEY_RIGHT; break; + case 0x25: actualKey = KEY_LEFT; break; + case 0x28: actualKey = KEY_DOWN; break; + case 0x26: actualKey = KEY_UP; break; + case 0x70: actualKey = KEY_F1; break; + case 0x71: actualKey = KEY_F2; break; + case 0x72: actualKey = KEY_F3; break; + case 0x73: actualKey = KEY_F4; break; + case 0x74: actualKey = KEY_F5; break; + case 0x75: actualKey = KEY_F6; break; + case 0x76: actualKey = KEY_F7; break; + case 0x77: actualKey = KEY_F8; break; + case 0x78: actualKey = KEY_F9; break; + case 0x79: actualKey = KEY_F10; break; + case 0x7A: actualKey = KEY_F11; break; + case 0x7B: actualKey = KEY_F12; break; + case 0xA0: actualKey = KEY_LEFT_SHIFT; break; + case 0xA2: actualKey = KEY_LEFT_CONTROL; break; + case 0xA4: actualKey = KEY_LEFT_ALT; break; + case 0xA1: actualKey = KEY_RIGHT_SHIFT; break; + case 0xA3: actualKey = KEY_RIGHT_CONTROL; break; + case 0xA5: actualKey = KEY_RIGHT_ALT; break; + case 0x30: actualKey = KEY_ZERO; break; + case 0x31: actualKey = KEY_ONE; break; + case 0x32: actualKey = KEY_TWO; break; + case 0x33: actualKey = KEY_THREE; break; + case 0x34: actualKey = KEY_FOUR; break; + case 0x35: actualKey = KEY_FIVE; break; + case 0x36: actualKey = KEY_SIX; break; + case 0x37: actualKey = KEY_SEVEN; break; + case 0x38: actualKey = KEY_EIGHT; break; + case 0x39: actualKey = KEY_NINE; break; + case 0x41: actualKey = KEY_A; break; + case 0x42: actualKey = KEY_B; break; + case 0x43: actualKey = KEY_C; break; + case 0x44: actualKey = KEY_D; break; + case 0x45: actualKey = KEY_E; break; + case 0x46: actualKey = KEY_F; break; + case 0x47: actualKey = KEY_G; break; + case 0x48: actualKey = KEY_H; break; + case 0x49: actualKey = KEY_I; break; + case 0x4A: actualKey = KEY_J; break; + case 0x4B: actualKey = KEY_K; break; + case 0x4C: actualKey = KEY_L; break; + case 0x4D: actualKey = KEY_M; break; + case 0x4E: actualKey = KEY_N; break; + case 0x4F: actualKey = KEY_O; break; + case 0x50: actualKey = KEY_P; break; + case 0x51: actualKey = KEY_Q; break; + case 0x52: actualKey = KEY_R; break; + case 0x53: actualKey = KEY_S; break; + case 0x54: actualKey = KEY_T; break; + case 0x55: actualKey = KEY_U; break; + case 0x56: actualKey = KEY_V; break; + case 0x57: actualKey = KEY_W; break; + case 0x58: actualKey = KEY_X; break; + case 0x59: actualKey = KEY_Y; break; + case 0x5A: actualKey = KEY_Z; break; + default: break; + } + + if (actualKey > -1) currentKeyState[actualKey] = msg->paramChar0; + + } break; + case UWP_MSG_REGISTER_CLICK: currentMouseState[msg->paramInt0] = msg->paramChar0; break; + case UWP_MSG_SCROLL_WHEEL_UPDATE: currentMouseWheelY += msg->paramInt0; break; + case UWP_MSG_UPDATE_MOUSE_LOCATION: mousePosition = msg->paramVector0; break; + case UWP_MSG_SET_GAMEPAD_ACTIVE: if (msg->paramInt0 < MAX_GAMEPADS) gamepadReady[msg->paramInt0] = msg->paramBool0; break; + case UWP_MSG_SET_GAMEPAD_BUTTON: + { + if ((msg->paramInt0 < MAX_GAMEPADS) && (msg->paramInt1 < MAX_GAMEPAD_BUTTONS)) currentGamepadState[msg->paramInt0][msg->paramInt1] = msg->paramChar0; + } break; + case UWP_MSG_SET_GAMEPAD_AXIS: + { + if ((msg->paramInt0 < MAX_GAMEPADS) && (msg->paramInt1 < MAX_GAMEPAD_AXIS)) gamepadAxisState[msg->paramInt0][msg->paramInt1] = msg->paramFloat0; + + // Register buttons for 2nd triggers + currentGamepadState[msg->paramInt0][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(gamepadAxisState[msg->paramInt0][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1); + currentGamepadState[msg->paramInt0][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(gamepadAxisState[msg->paramInt0][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1); + } break; + case UWP_MSG_SET_DISPLAY_DIMS: + { + displayWidth = msg->paramVector0.x; + displayHeight = msg->paramVector0.y; + } break; + case UWP_MSG_HANDLE_RESIZE: + { + eglQuerySurface(display, surface, EGL_WIDTH, &screenWidth); + eglQuerySurface(display, surface, EGL_HEIGHT, &screenHeight); + + // If window is resized, viewport and projection matrix needs to be re-calculated + rlViewport(0, 0, screenWidth, screenHeight); // Set viewport width and height + rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix + rlLoadIdentity(); // Reset current matrix (PROJECTION) + rlOrtho(0, screenWidth, screenHeight, 0, 0.0f, 1.0f); // Orthographic projection mode with top-left corner at (0,0) + rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix + rlLoadIdentity(); // Reset current matrix (MODELVIEW) + rlClearScreenBuffers(); // Clear screen buffers (color and depth) + + // Window size must be updated to be used on 3D mode to get new aspect ratio (BeginMode3D()) + // NOTE: Be careful! GLFW3 will choose the closest fullscreen resolution supported by current monitor, + // for example, if reescaling back to 800x450 (desired), it could set 720x480 (closest fullscreen supported) + currentWidth = screenWidth; + currentHeight = screenHeight; + + // NOTE: Postprocessing texture is not scaled to new size + + windowResized = true; + + } break; + case UWP_MSG_SET_GAME_TIME: currentTime = msg->paramDouble0; break; + default: break; + } + + DeleteUWPMessage(msg); //Delete, we are done + } +#endif // PLATFORM_UWP + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) // Mouse input polling double mouseX; @@ -3090,7 +3432,7 @@ static void PollInputEvents(void) #if defined(PLATFORM_DESKTOP) // Check if gamepads are ready - // NOTE: We do it here in case of disconection + // NOTE: We do it here in case of disconnection for (int i = 0; i < MAX_GAMEPADS; i++) { if (glfwJoystickPresent(i)) gamepadReady[i] = true; @@ -3107,36 +3449,42 @@ static void PollInputEvents(void) // Get current gamepad state // NOTE: There is no callback available, so we get it manually - const unsigned char *buttons; - int buttonsCount; - - buttons = glfwGetJoystickButtons(i, &buttonsCount); + // Get remapped buttons + GLFWgamepadstate state; + 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 < buttonsCount) && (buttonsCount < MAX_GAMEPAD_BUTTONS); k++) + for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) { + const GamepadButton button = GetGamepadButton(k); + if (buttons[k] == GLFW_PRESS) { - currentGamepadState[i][k] = 1; - lastGamepadButtonPressed = k; + currentGamepadState[i][button] = 1; + lastGamepadButtonPressed = button; } - else currentGamepadState[i][k] = 0; + else currentGamepadState[i][button] = 0; } // Get current axis state - const float *axes; - int axisCount = 0; - - axes = glfwGetJoystickAxes(i, &axisCount); + const float *axes = state.axes; - for (int k = 0; (axes != NULL) && (k < axisCount) && (k < MAX_GAMEPAD_AXIS); k++) + for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) { - gamepadAxisState[i][k] = axes[k]; + const int axis = GetGamepadAxis(k); + gamepadAxisState[i][axis] = axes[k]; } - gamepadAxisCount = axisCount; + // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis) + currentGamepadState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(gamepadAxisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1); + currentGamepadState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(gamepadAxisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1); + + gamepadAxisCount = GLFW_GAMEPAD_AXIS_LAST; } } + windowResized = false; + #if defined(SUPPORT_EVENTS_WAITING) glfwWaitEvents(); #else @@ -3148,7 +3496,8 @@ static void PollInputEvents(void) // NOTE: GLFW3 joystick functionality not available in web #if defined(PLATFORM_WEB) // Get number of gamepads connected - int numGamepads = emscripten_get_num_gamepads(); + 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++) { @@ -3164,20 +3513,22 @@ static void PollInputEvents(void) // Register buttons data for every connected gamepad for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++) { + const GamepadButton button = GetGamepadButton(j); if (gamepadState.digitalButton[j] == 1) { - currentGamepadState[i][j] = 1; - lastGamepadButtonPressed = j; + currentGamepadState[i][button] = 1; + lastGamepadButtonPressed = button; } - else currentGamepadState[i][j] = 0; + else currentGamepadState[i][button] = 0; - //printf("Gamepad %d, button %d: Digital: %d, Analog: %g\n", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); + //TraceLog(LOG_DEBUG, "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++) { - gamepadAxisState[i][j] = gamepadState.axis[j]; + const int axis = GetGamepadAxis(j); + gamepadAxisState[i][axis] = gamepadState.axis[j]; } gamepadAxisCount = gamepadState.numAxes; @@ -3192,7 +3543,7 @@ static void PollInputEvents(void) // Poll Events (registered events) // NOTE: Activity is paused if not enabled (appEnabled) - while ((ident = ALooper_pollAll(appEnabled ? 0 : -1, NULL, &events,(void**)&source)) >= 0) + while ((ident = ALooper_pollAll(appEnabled? 0 : -1, NULL, &events,(void**)&source)) >= 0) { // Process this event if (source != NULL) source->process(androidApp, source); @@ -3207,13 +3558,12 @@ static void PollInputEvents(void) } #endif -#if defined(PLATFORM_RPI) - // NOTE: Mouse input events polling is done asynchonously in another pthread - MouseThread() - +#if defined(PLATFORM_RPI) && defined(SUPPORT_SSH_KEYBOARD_RPI) // NOTE: Keyboard reading could be done using input_event(s) reading or just read from stdin, - // we use method 2 (stdin) but maybe in a future we should change to method 1... + // we now use both methods inside here. 2nd method is still used for legacy purposes (Allows for input trough SSH console) 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 } @@ -3264,8 +3614,8 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i #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))); + // 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, "End animated GIF recording"); @@ -3393,26 +3743,17 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) // NOTE: Window resizing not allowed by default static void WindowSizeCallback(GLFWwindow *window, int width, int height) { - // If window is resized, viewport and projection matrix needs to be re-calculated - rlViewport(0, 0, width, height); // Set viewport width and height - rlMatrixMode(RL_PROJECTION); // Switch to PROJECTION matrix - rlLoadIdentity(); // Reset current matrix (PROJECTION) - rlOrtho(0, width, height, 0, 0.0f, 1.0f); // Orthographic projection mode with top-left corner at (0,0) - rlMatrixMode(RL_MODELVIEW); // Switch back to MODELVIEW matrix - rlLoadIdentity(); // Reset current matrix (MODELVIEW) - rlClearScreenBuffers(); // Clear screen buffers (color and depth) - - // Window size must be updated to be used on 3D mode to get new aspect ratio (BeginMode3D()) - // NOTE: Be careful! GLFW3 will choose the closest fullscreen resolution supported by current monitor, - // for example, if reescaling back to 800x450 (desired), it could set 720x480 (closest fullscreen supported) + SetupViewport(width, height); // Reset viewport and projection matrix for new size + + // Set current screen size screenWidth = width; screenHeight = height; - renderWidth = width; - renderHeight = height; currentWidth = width; currentHeight = height; // NOTE: Postprocessing texture is not scaled to new size + + windowResized = true; } // GLFW3 WindowIconify Callback, runs when window is minimized/restored @@ -3429,11 +3770,11 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths { ClearDroppedFiles(); - dropFilesPath = (char **)malloc(sizeof(char *)*count); + dropFilesPath = (char **)RL_MALLOC(sizeof(char *)*count); for (int i = 0; i < count; i++) { - dropFilesPath[i] = (char *)malloc(sizeof(char)*MAX_FILEPATH_LENGTH); // Max path length using MAX_FILEPATH_LENGTH set to 256 char + dropFilesPath[i] = (char *)RL_MALLOC(sizeof(char)*MAX_FILEPATH_LENGTH); strcpy(dropFilesPath[i], paths[i]); } @@ -3679,7 +4020,6 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) ProcessGestureEvent(gestureEvent); } #else - // Support only simple touch position if (flags == AMOTION_EVENT_ACTION_DOWN) { @@ -3771,18 +4111,19 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent y = touchEvent->touches[i].canvasY; } - printf("%s, numTouches: %d %s%s%s%s\n", emscripten_event_type_to_string(eventType), event->numTouches, - event->ctrlKey ? " CTRL" : "", event->shiftKey ? " SHIFT" : "", event->altKey ? " ALT" : "", event->metaKey ? " META" : ""); + TraceLog(LOG_DEBUG, "%s, numTouches: %d %s%s%s%s", emscripten_event_type_to_string(eventType), event->numTouches, + event->ctrlKey? " CTRL" : "", event->shiftKey? " SHIFT" : "", event->altKey? " ALT" : "", event->metaKey? " META" : ""); for (int i = 0; i < event->numTouches; ++i) { const EmscriptenTouchPoint *t = &event->touches[i]; - printf(" %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)\n", + TraceLog(LOG_DEBUG, " %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld)", t->identifier, t->screenX, t->screenY, t->clientX, t->clientY, t->pageX, t->pageY, t->isChanged, t->onTarget, t->canvasX, t->canvasY); } */ +#if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent; // Register touch actions @@ -3817,6 +4158,17 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent // Gesture data is sent to gestures system for processing ProcessGestureEvent(gestureEvent); +#else + // Support only simple touch position + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) + { + // Get first touch position + touchPosition[0] = (Vector2){ touchEvent->touches[0].targetX, touchEvent->touches[0].targetY }; + + touchPosition[0].x /= (float)GetScreenWidth(); + touchPosition[0].y /= (float)GetScreenHeight(); + } +#endif return 1; } @@ -3825,12 +4177,12 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) { /* - printf("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"\n", - eventType != 0 ? emscripten_event_type_to_string(eventType) : "Gamepad state", + TraceLog(LOG_DEBUG, "%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) printf("Axis %d: %g\n", i, gamepadEvent->axis[i]); - for(int i = 0; i < gamepadEvent->numButtons; ++i) printf("Button %d: Digital: %d, Analog: %g\n", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + for(int i = 0; i < gamepadEvent->numAxes; ++i) TraceLog(LOG_DEBUG, "Axis %d: %g", i, gamepadEvent->axis[i]); + for(int i = 0; i < gamepadEvent->numButtons; ++i) TraceLog(LOG_DEBUG, "Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); */ if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) gamepadReady[gamepadEvent->index] = true; @@ -3843,6 +4195,8 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE #endif #if defined(PLATFORM_RPI) + +#if defined(SUPPORT_SSH_KEYBOARD_RPI) // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { @@ -3905,15 +4259,14 @@ static void ProcessKeyboard(void) // Reset pressed keys array (it will be filled below) for (int i = 0; i < 512; i++) currentKeyState[i] = 0; + // Check keys from event input workers (This is the new keyboard reading method) + for (int i = 0; i < 512; i++) currentKeyState[i] = currentKeyStateEvdev[i]; + // Fill all read bytes (looking for keys) for (int i = 0; i < bufferByteCount; i++) { TraceLog(LOG_DEBUG, "Bytes on keysBuffer: %i", bufferByteCount); - //printf("Key(s) bytes: "); - //for (int i = 0; i < bufferByteCount; i++) printf("0x%02x ", keysBuffer[i]); - //printf("\n"); - // 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) @@ -4004,9 +4357,10 @@ static void RestoreKeyboard(void) // Reconfigure keyboard to default mode ioctl(STDIN_FILENO, KDSKBMODE, defaultKeyboardMode); } +#endif //SUPPORT_SSH_KEYBOARD_RPI -// Mouse initialization (including mouse thread) -static void InitMouse(void) +// Initialise user input from evdev(/dev/input/event<N>) this means mouse, keyboard or gamepad devices +static void InitEvdevInput(void) { char path[MAX_FILEPATH_LENGTH]; DIR *directory; @@ -4018,9 +4372,15 @@ static void InitMouse(void) touchPosition[i].x = -1; touchPosition[i].y = -1; } + // Reset keypress buffer + lastKeyPressedEvdev.Head = 0; + lastKeyPressedEvdev.Tail = 0; + // Reset keyboard key state + for (int i = 0; i < 512; i++) currentKeyStateEvdev[i] = 0; // Open the linux directory of "/dev/input" directory = opendir(DEFAULT_EVDEV_PATH); + if (directory) { while ((entity = readdir(directory)) != NULL) @@ -4186,15 +4546,15 @@ static void EventThreadSpawn(char *device) // Decide what to do with the device //------------------------------------------------------------------------------------------------------- - if (worker->isTouch || worker->isMouse) + if (worker->isTouch || worker->isMouse || worker->isKeyboard) { // Looks like a interesting device TraceLog(LOG_INFO, "Opening input device [%s] (%s%s%s%s%s)", device, - worker->isMouse ? "mouse " : "", - worker->isMultitouch ? "multitouch " : "", - worker->isTouch ? "touchscreen " : "", - worker->isGamepad ? "gamepad " : "", - worker->isKeyboard ? "keyboard " : ""); + worker->isMouse? "mouse " : "", + worker->isMultitouch? "multitouch " : "", + worker->isTouch? "touchscreen " : "", + worker->isGamepad? "gamepad " : "", + worker->isKeyboard? "keyboard " : ""); // Create a thread for this device int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker); @@ -4236,13 +4596,36 @@ static void EventThreadSpawn(char *device) // Input device events reading thread static void *EventThread(void *arg) { + // Scancode to keycode mapping for US keyboards + // TODO: Probably 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 keymap_US[] = + { 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, }; + struct input_event event; - GestureEvent gestureEvent; InputEventWorker *worker = (InputEventWorker *)arg; - bool GestureNeedsUpdate = false; + + int touchAction = -1; + bool gestureUpdate = false; + int keycode; while (!windowShouldClose) { + // Try to read data from the device and only continue if successful if (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) { // Relative movement parsing @@ -4252,22 +4635,25 @@ static void *EventThread(void *arg) { mousePosition.x += event.value; touchPosition[0].x = mousePosition.x; - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } if (event.code == REL_Y) { mousePosition.y += event.value; touchPosition[0].y = mousePosition.y; - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; - } - if (event.code == REL_WHEEL) - { - currentMouseWheelY += event.value; + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } + + if (event.code == REL_WHEEL) currentMouseWheelY += event.value; } // Absolute movement parsing @@ -4277,38 +4663,39 @@ static void *EventThread(void *arg) if (event.code == ABS_X) { mousePosition.x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } if (event.code == ABS_Y) { mousePosition.y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange - gestureEvent.touchAction = TOUCH_MOVE; - GestureNeedsUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + touchAction = TOUCH_MOVE; + gestureUpdate = true; + #endif } // Multitouch movement - if (event.code == ABS_MT_SLOT) - { - worker->touchSlot = event.value; // Remeber the slot number for the folowing events - } + if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remeber the slot number for the folowing events if (event.code == ABS_MT_POSITION_X) { - if (worker->touchSlot < MAX_TOUCH_POINTS) - touchPosition[worker->touchSlot].x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange + if (worker->touchSlot < MAX_TOUCH_POINTS) touchPosition[worker->touchSlot].x = (event.value - worker->absRange.x)*screenWidth/worker->absRange.width; // Scale acording to absRange } if (event.code == ABS_MT_POSITION_Y) { - if (worker->touchSlot < MAX_TOUCH_POINTS) - touchPosition[worker->touchSlot].y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange + if (worker->touchSlot < MAX_TOUCH_POINTS) touchPosition[worker->touchSlot].y = (event.value - worker->absRange.y)*screenHeight/worker->absRange.height; // Scale acording to absRange } if (event.code == ABS_MT_TRACKING_ID) { - if ( (event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS) ) + if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS)) { // Touch has ended for this point touchPosition[worker->touchSlot].x = -1; @@ -4320,17 +4707,43 @@ static void *EventThread(void *arg) // Button parsing if (event.type == EV_KEY) { - if((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) + // Mouse button parsing + if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) { currentMouseStateEvdev[MOUSE_LEFT_BUTTON] = event.value; - if (event.value > 0) gestureEvent.touchAction = TOUCH_DOWN; - else gestureEvent.touchAction = TOUCH_UP; - GestureNeedsUpdate = true; + + #if defined(SUPPORT_GESTURES_SYSTEM) + if (event.value > 0) touchAction = TOUCH_DOWN; + else touchAction = TOUCH_UP; + gestureUpdate = true; + #endif } if (event.code == BTN_RIGHT) currentMouseStateEvdev[MOUSE_RIGHT_BUTTON] = event.value; if (event.code == BTN_MIDDLE) currentMouseStateEvdev[MOUSE_MIDDLE_BUTTON] = event.value; + + // Keyboard button parsing + if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 + { + keycode = keymap_US[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode + + // Make sure we got a valid keycode + if ((keycode > 0) && (keycode < sizeof(currentKeyState))) + { + // Store the key information for raylib to later use + currentKeyStateEvdev[keycode] = event.value; + if (event.value > 0) + { + // Add the key int the fifo + lastKeyPressedEvdev.Contents[lastKeyPressedEvdev.Head] = keycode; // Put the data at the front of the fifo snake + lastKeyPressedEvdev.Head = (lastKeyPressedEvdev.Head + 1) & 0x07; // Increment the head pointer forwards and binary wraparound after 7 (fifo is 8 elements long) + // TODO: This fifo is not fully threadsafe with multiple writers, so multiple keyboards hitting a key at the exact same time could miss a key (double write to head before it was incremented) + } + + TraceLog(LOG_DEBUG, "KEY%s ScanCode: %4i KeyCode: %4i",event.value == 0 ? "UP":"DOWN", event.code, keycode); + } + } } // Screen confinement @@ -4341,22 +4754,31 @@ static void *EventThread(void *arg) if (mousePosition.y > screenHeight/mouseScale.y) mousePosition.y = screenHeight/mouseScale.y; // Gesture update - if (GestureNeedsUpdate) + if (gestureUpdate) { + #if defined(SUPPORT_GESTURES_SYSTEM) + GestureEvent gestureEvent = { 0 }; + gestureEvent.pointCount = 0; + gestureEvent.touchAction = touchAction; + if (touchPosition[0].x >= 0) gestureEvent.pointCount++; if (touchPosition[1].x >= 0) gestureEvent.pointCount++; if (touchPosition[2].x >= 0) gestureEvent.pointCount++; if (touchPosition[3].x >= 0) gestureEvent.pointCount++; + gestureEvent.pointerId[0] = 0; gestureEvent.pointerId[1] = 1; gestureEvent.pointerId[2] = 2; gestureEvent.pointerId[3] = 3; + gestureEvent.position[0] = touchPosition[0]; gestureEvent.position[1] = touchPosition[1]; gestureEvent.position[2] = touchPosition[2]; gestureEvent.position[3] = touchPosition[3]; + ProcessGestureEvent(gestureEvent); + #endif } } else @@ -4464,7 +4886,7 @@ static void *GamepadThread(void *arg) // Plays raylib logo appearing animation static void LogoAnimation(void) { -#if !defined(PLATFORM_WEB) +#if !defined(PLATFORM_WEB) && !defined(PLATFORM_UWP) int logoPositionX = screenWidth/2 - 128; int logoPositionY = screenHeight/2 - 128; @@ -4573,4 +4995,4 @@ static void LogoAnimation(void) #endif showLogo = false; // Prevent for repeating when reloading window (Android) -} +}
\ No newline at end of file |
