2014-09-17 00:51:31 +04:00
/**********************************************************************************************
2013-11-19 02:38:44 +04:00
*
2017-02-16 02:50:02 +03:00
* raylib . core - Basic functions to manage windows , OpenGL context and input on multiple platforms
2014-09-03 18:51:28 +04:00
*
2018-11-06 17:10:50 +03:00
* PLATFORMS SUPPORTED :
2017-11-10 14:37:53 +03:00
* - PLATFORM_DESKTOP : Windows ( Win32 , Win64 )
* - PLATFORM_DESKTOP : Linux ( X11 desktop mode )
2018-06-23 01:33:48 +03:00
* - PLATFORM_DESKTOP : FreeBSD , OpenBSD , NetBSD , DragonFly ( X11 desktop )
2017-11-10 14:37:53 +03:00
* - PLATFORM_DESKTOP : OSX / macOS
2018-11-06 17:10:50 +03:00
* - PLATFORM_ANDROID : Android 4.0 ( ARM , ARM64 )
2017-11-10 14:37:53 +03:00
* - PLATFORM_RPI : Raspberry Pi 0 , 1 , 2 , 3 ( Raspbian )
* - PLATFORM_WEB : HTML5 with asm . js ( Chrome , Firefox )
* - PLATFORM_UWP : Windows 10 App , Windows Phone , Xbox One
2013-11-19 02:38:44 +04:00
*
2017-02-16 02:50:02 +03:00
* CONFIGURATION :
*
* # define PLATFORM_DESKTOP
2018-06-23 01:33:48 +03:00
* Windowing and input system configured for desktop platforms : Windows , Linux , OSX , FreeBSD , OpenBSD , NetBSD , DragonFly
2017-02-16 02:50:02 +03:00
* NOTE : Oculus Rift CV1 requires PLATFORM_DESKTOP for mirror rendering - View [ rlgl ] module to enable it
*
* # 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_RPI
2018-11-06 17:10:50 +03:00
* Windowing and input system configured for Raspberry Pi i native mode ( no X . org required , tested on Raspbian ) ,
2017-12-05 16:01:35 +03:00
* graphic device is managed by EGL and inputs are processed is raw mode , reading from / dev / input /
2017-02-16 02:50:02 +03:00
*
* # 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 .
*
2017-11-10 14:37:53 +03:00
* # define PLATFORM_UWP
* Universal Windows Platform support , using OpenGL ES 2.0 through ANGLE on multiple Windows platforms ,
* including Windows 10 App , Windows Phone and Xbox One platforms .
*
2017-03-21 15:22:59 +03:00
* # define SUPPORT_DEFAULT_FONT ( default )
2017-02-16 02:50:02 +03:00
* 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 )
*
2017-03-21 15:22:59 +03:00
* # define SUPPORT_CAMERA_SYSTEM
* Camera module is included ( camera . h ) and multiple predefined cameras are available : free , 1 st / 3 rd person , orbital
2017-02-16 02:50:02 +03:00
*
2017-03-21 15:22:59 +03:00
* # define SUPPORT_GESTURES_SYSTEM
* Gestures module is included ( gestures . h ) to support gestures detection : tap , hold , swipe , drag
2017-02-16 02:50:02 +03:00
*
* # define SUPPORT_MOUSE_GESTURES
* Mouse gestures are directly mapped like touches and processed by gestures system .
*
2017-05-08 03:37:37 +03:00
* # define SUPPORT_BUSY_WAIT_LOOP
2018-04-07 23:29:53 +03:00
* Use busy wait loop for timing sync , if not defined , a high - resolution timer is setup and used
2017-05-08 03:37:37 +03:00
*
2018-11-01 01:19:29 +03:00
* # define SUPPORT_EVENTS_WAITING
* Wait for events passively ( sleeping while no events ) instead of polling them actively every frame
*
2018-04-29 12:37:39 +03:00
* # define SUPPORT_SCREEN_CAPTURE
* Allow automatic screen capture of current screen pressing F12 , defined in KeyCallback ( )
*
2017-05-18 19:57:11 +03:00
* # define SUPPORT_GIF_RECORDING
* Allow automatic gif recording of current screen pressing CTRL + F12 , defined in KeyCallback ( )
*
2017-02-16 02:50:02 +03:00
* DEPENDENCIES :
2018-06-23 01:33:48 +03:00
* rglfw - Manage graphic device , OpenGL context and inputs on PLATFORM_DESKTOP ( Windows , Linux , OSX . FreeBSD , OpenBSD , NetBSD , DragonFly )
2017-12-05 16:01:35 +03:00
* raymath - 3 D math functionality ( Vector2 , Vector3 , Matrix , Quaternion )
2017-01-29 01:02:30 +03:00
* camera - Multiple 3 D camera modes ( free , orbital , 1 st person , 3 rd person )
2016-11-16 20:46:13 +03:00
* gestures - Gestures system for touch - ready devices ( or simulated from mouse inputs )
2014-09-17 00:51:31 +04:00
*
2016-11-16 20:46:13 +03:00
*
2017-02-16 02:50:02 +03:00
* LICENSE : zlib / libpng
2016-11-16 20:46:13 +03:00
*
2019-04-08 13:25:13 +03:00
* Copyright ( c ) 2013 - 2019 Ramon Santamaria ( @ raysan5 )
2014-09-03 18:51:28 +04:00
*
* This software is provided " as-is " , without any express or implied warranty . In no event
2013-11-23 16:30:54 +04:00
* will the authors be held liable for any damages arising from the use of this software .
2013-11-19 02:38:44 +04:00
*
2014-09-03 18:51:28 +04:00
* Permission is granted to anyone to use this software for any purpose , including commercial
2013-11-23 16:30:54 +04:00
* applications , and to alter it and redistribute it freely , subject to the following restrictions :
2013-11-19 02:38:44 +04:00
*
2014-09-03 18:51:28 +04:00
* 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
2013-11-23 16:30:54 +04:00
* in the product documentation would be appreciated but is not required .
2013-11-19 02:38:44 +04:00
*
2013-11-23 16:30:54 +04:00
* 2. Altered source versions must be plainly marked as such , and must not be misrepresented
* as being the original software .
2013-11-19 02:38:44 +04:00
*
2013-11-23 16:30:54 +04:00
* 3. This notice may not be removed or altered from any source distribution .
2013-11-19 02:38:44 +04:00
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-04-29 12:49:10 +03:00
# include "raylib.h" // Declares module functions
2016-12-17 21:05:40 +03:00
2018-12-18 02:20:08 +03:00
// Check if config flags have been externally provided on compilation line
# if !defined(EXTERNAL_CONFIG_FLAGS)
# include "config.h" // Defines module configuration flags
2019-03-12 18:00:26 +03:00
# else
# define RAYLIB_VERSION "2.5"
2018-12-18 02:20:08 +03:00
# endif
2017-12-10 23:11:04 +03:00
# if (defined(__linux__) || defined(PLATFORM_WEB)) && _POSIX_C_SOURCE < 199309L
2017-11-23 00:47:57 +03:00
# undef _POSIX_C_SOURCE
# define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.
# endif
raymath.h: Use C99 inline semantics
RAYMATH_EXTERN_INLINE was renamed to RAYMATH_HEADER_ONLY, which user code
may define if they want to use it as header-only library. If multiple
files in the same project define RAYMATH_HEADER_ONLY, they might each
have duplicate out-of-line definitions of the same functions.
By default, raymath.h exposes inline definitions, which instructs the
compiler _not_ to generate out-of-line definitons, if out-of-line
definitions are required, those of the file defined with
RAYLIB_IMPLEMENTATION are used instead. There may be only one such file.
In C++ mode, the compiler will select only one out-of-line definition
automatically, so no need to define a RAYLIB_IMPLEMENTATION.
Unfortunately, we have to remove raymath function declaration from
raylib.h as those declarations would lead to duplicate out-of-line
definitions which would yield linker errors. This problem didn't
exist with GNU89 or C++, because there multiple defintions are ok,
but in C99 they aren't.
2018-02-22 04:05:00 +03:00
# define RAYMATH_IMPLEMENTATION // Define external out-of-line implementation of raymath here
# include "raymath.h" // Required for: Vector3 and Matrix functions
2018-07-16 18:53:47 +03:00
# define RLGL_IMPLEMENTATION
2018-04-29 12:49:10 +03:00
# include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
2018-07-16 18:53:47 +03:00
2018-04-29 12:49:10 +03:00
# include "utils.h" // Required for: fopen() Android mapping
2016-08-06 17:32:46 +03:00
2017-03-21 15:22:59 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
# define GESTURES_IMPLEMENTATION
# include "gestures.h" // Gestures detection functionality
# endif
2016-08-06 17:32:46 +03:00
2017-03-21 15:22:59 +03:00
# if defined(SUPPORT_CAMERA_SYSTEM) && !defined(PLATFORM_ANDROID)
2016-08-26 20:40:37 +03:00
# define CAMERA_IMPLEMENTATION
2017-05-18 19:57:11 +03:00
# include "camera.h" // Camera system functionality
# endif
# if defined(SUPPORT_GIF_RECORDING)
2017-10-02 14:06:04 +03:00
# define RGIF_IMPLEMENTATION
2018-04-29 12:49:10 +03:00
# include "external/rgif.h" // Support GIF recording
2016-08-26 20:40:37 +03:00
# endif
2016-08-10 13:55:54 +03:00
2013-11-23 16:30:54 +04:00
# include <stdio.h> // Standard input / output lib
2016-11-01 16:39:57 +03:00
# 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
# include <time.h> // Required for: time() - Android/RPI hi-res timer (NOTE: Linux only!)
2018-05-04 17:54:05 +03:00
# include <math.h> // Required for: tan() [Used in BeginMode3D() to set perspective]
2017-03-29 01:35:42 +03:00
# include <string.h> // Required for: strrchr(), strcmp()
2016-11-01 16:39:57 +03:00
//#include <errno.h> // Macros for reporting and retrieving error conditions through error codes
2018-03-16 15:09:49 +03:00
# include <ctype.h> // Required for: tolower() [Used in IsFileExtension()]
2018-10-12 15:53:36 +03:00
# include <sys/stat.h> // Required for stat() [Used in GetLastWriteTime()]
2018-10-08 17:25:47 +03:00
2018-11-19 17:18:11 +03:00
# if defined(PLATFORM_DESKTOP) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
2018-10-08 19:38:39 +03:00
# include "external/dirent.h" // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()]
2018-10-08 17:25:47 +03:00
# else
# include <dirent.h> // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()]
# endif
2013-11-19 02:38:44 +04:00
2017-12-24 18:12:52 +03:00
# if defined(_WIN32)
2017-05-11 17:24:40 +03:00
# include <direct.h> // Required for: _getch(), _chdir()
# define GETCWD _getcwd // NOTE: MSDN recommends not to use getcwd(), chdir()
# define CHDIR _chdir
2018-10-14 15:14:04 +03:00
# include <io.h> // Required for _access() [Used in FileExists()]
2017-05-11 17:24:40 +03:00
# else
2018-10-14 15:14:04 +03:00
# include "unistd.h" // Required for: getch(), chdir() (POSIX), access()
2017-05-11 17:24:40 +03:00
# define GETCWD getcwd
# define CHDIR chdir
# endif
2018-10-08 17:12:09 +03:00
# if defined(PLATFORM_DESKTOP)
2018-10-08 19:08:39 +03:00
# define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3
// NOTE: Already provided by rlgl implementation (on glad.h)
2017-01-28 02:56:45 +03:00
# include <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management
2018-05-05 00:03:56 +03:00
// NOTE: GLFW3 already includes gl.h (OpenGL) headers
2018-10-08 17:12:09 +03:00
2018-10-08 13:29:02 +03:00
// Support retrieving native window handlers
# if defined(_WIN32)
2018-10-08 14:29:42 +03:00
# define GLFW_EXPOSE_NATIVE_WIN32
# include <GLFW/glfw3native.h> // WARNING: It requires customization to avoid windows.h inclusion!
2018-11-06 17:10:50 +03:00
2018-10-08 17:12:09 +03:00
# if !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
2018-10-25 17:09:38 +03:00
2018-10-08 13:29:02 +03:00
# elif defined(__linux__)
2018-10-08 17:12:09 +03:00
# include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
2018-11-06 17:10:50 +03:00
2018-10-08 17:12:09 +03:00
//#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()
# elif defined(__APPLE__)
# include <unistd.h> // Required for: usleep()
2018-11-06 17:10:50 +03:00
2018-10-08 19:51:41 +03:00
//#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition
2018-10-27 12:11:54 +03:00
# include <GLFW/glfw3native.h> // Required for: glfwGetCocoaWindow()
2018-10-25 17:16:44 +03:00
# endif
# endif
# if defined(__linux__)
2019-03-13 12:07:01 +03:00
# include <linux/limits.h> // for NAME_MAX and PATH_MAX defines
# define MAX_FILEPATH_LENGTH PATH_MAX // Use Linux define (4096)
2018-10-25 17:16:44 +03:00
# else
2019-03-13 12:07:01 +03:00
# define MAX_FILEPATH_LENGTH 512 // Use common value
2014-09-17 00:51:31 +04:00
# endif
# if defined(PLATFORM_ANDROID)
2016-10-18 01:15:23 +03:00
//#include <android/sensor.h> // Android sensors functions (accelerometer, gyroscope, light...)
2014-09-17 00:51:31 +04:00
# include <android/window.h> // Defines AWINDOW_FLAG_FULLSCREEN and others
2016-01-03 15:01:21 +03:00
# include <android_native_app_glue.h> // Defines basic app state struct and manages activity
2014-03-25 15:40:35 +04:00
2014-09-17 00:51:31 +04:00
# include <EGL/egl.h> // Khronos EGL library - Native platform display device control functions
# include <GLES2/gl2.h> // Khronos OpenGL ES 2.0 library
# endif
# if defined(PLATFORM_RPI)
# include <fcntl.h> // POSIX file control definitions - open(), creat(), fcntl()
# include <unistd.h> // POSIX standard function definitions - read(), close(), STDIN_FILENO
# include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
# include <pthread.h> // POSIX threads management (mouse input)
2018-10-21 02:09:17 +03:00
# include <dirent.h> // POSIX directory browsing
2014-09-17 00:51:31 +04:00
# include <sys/ioctl.h> // UNIX System call for device-specific input/output operations - ioctl()
# include <linux/kd.h> // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
# include <linux/input.h> // Linux: Keycodes constants definition (KEY_A, ...)
2016-11-02 15:39:48 +03:00
# include <linux/joystick.h> // Linux: Joystick support library
2017-01-29 01:02:30 +03:00
2014-09-17 00:51:31 +04:00
# include "bcm_host.h" // Raspberry Pi VideoCore IV access functions
# include "EGL/egl.h" // Khronos EGL library - Native platform display device control functions
# include "EGL/eglext.h" // Khronos EGL library - Extensions
# include "GLES2/gl2.h" // Khronos OpenGL ES 2.0 library
# endif
2013-11-19 02:38:44 +04:00
2017-11-10 14:37:53 +03:00
# if defined(PLATFORM_UWP)
# include "EGL/egl.h" // Khronos EGL library - Native platform display device control functions
# include "EGL/eglext.h" // Khronos EGL library - Extensions
# include "GLES2/gl2.h" // Khronos OpenGL ES 2.0 library
# endif
2015-10-30 13:30:32 +03:00
# if defined(PLATFORM_WEB)
2018-10-08 17:12:09 +03:00
# define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL)
# include <GLFW/glfw3.h> // GLFW3 library: Windows, OpenGL context and Input management
# include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
2018-11-06 17:10:50 +03:00
2018-10-08 17:12:09 +03:00
# include <emscripten/emscripten.h> // Emscripten library - LLVM to JavaScript compiler
# include <emscripten/html5.h> // Emscripten HTML5 library
2015-10-30 13:30:32 +03:00
# endif
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
2016-02-19 02:16:16 +03:00
# if defined(PLATFORM_RPI)
2018-10-22 12:48:16 +03:00
# define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number
2016-02-19 02:16:16 +03:00
// Old device inputs system
2016-03-17 14:54:36 +03:00
# define DEFAULT_KEYBOARD_DEV STDIN_FILENO // Standard input
# define DEFAULT_GAMEPAD_DEV " / dev / input / js" // Gamepad input (base dev for all gamepads: js0, js1, ...)
2018-10-21 02:09:17 +03:00
# define DEFAULT_EVDEV_PATH " / dev / input / " // Path to the linux input events
2016-02-19 02:16:16 +03:00
// New device input events (evdev) (must be detected)
//#define DEFAULT_KEYBOARD_DEV "/dev/input/eventN"
//#define DEFAULT_MOUSE_DEV "/dev/input/eventN"
//#define DEFAULT_GAMEPAD_DEV "/dev/input/eventN"
2016-08-16 12:09:55 +03:00
2016-02-19 21:57:25 +03:00
# define MOUSE_SENSITIVITY 0.8f
2016-02-19 02:16:16 +03:00
# endif
2016-10-14 12:14:41 +03:00
# define MAX_GAMEPADS 4 // Max number of gamepads supported
2016-10-17 19:18:13 +03:00
# define MAX_GAMEPAD_BUTTONS 32 // Max bumber of buttons supported (per gamepad)
2016-10-14 12:14:41 +03:00
# define MAX_GAMEPAD_AXIS 8 // Max number of axis supported (per gamepad)
2017-03-21 15:22:59 +03:00
# define STORAGE_FILENAME "storage.data"
2016-10-09 14:09:08 +03:00
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
2016-06-14 18:16:20 +03:00
// ...
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
2018-10-08 13:29:02 +03:00
// Window/Graphics related variables
//-----------------------------------------------------------------------------------
2014-12-15 03:08:30 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
2014-09-17 00:51:31 +04:00
static GLFWwindow * window ; // Native window (graphic device)
2016-06-03 01:53:51 +03:00
# endif
2018-02-04 14:26:28 +03:00
static bool windowReady = false ; // Check if window has been initialized successfully
static bool windowMinimized = false ; // Check if window has been minimized
2019-03-05 00:58:20 +03:00
static bool windowResized = false ; // Check if window has been resized
2018-04-02 15:49:01 +03:00
static const char * windowTitle = NULL ; // Window text title...
2017-11-10 14:37:53 +03:00
2018-10-08 13:29:02 +03:00
static unsigned int displayWidth , displayHeight ; // Display width and height (monitor, device-screen, LCD, ...)
2019-01-24 04:08:51 +03:00
static int screenWidth , screenHeight ; // Screen width and height (used render area)
2018-10-08 13:29:02 +03:00
static int renderWidth , renderHeight ; // Framebuffer width and height (render area, including black bars if required)
2019-02-04 19:10:12 +03:00
static int currentWidth , currentHeight ; // Current render width and height, it could change on BeginTextureMode()
2018-10-08 13:29:02 +03:00
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)
# if defined(PLATFORM_RPI)
static EGL_DISPMANX_WINDOW_T nativeWindow ; // Native window (graphic device)
# endif
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
static EGLDisplay display ; // Native display device (physical screen connection)
static EGLSurface surface ; // Surface to draw on, framebuffers (connected to context)
static EGLContext context ; // Graphic context, mode in which drawing can be done
static EGLConfig config ; // Graphic config
static uint64_t baseTime ; // Base time measure for hi-res timer
static bool windowShouldClose = false ; // Flag to set window for closing
# endif
# if defined(PLATFORM_UWP)
extern EGLNativeWindowType uwpWindow ; // Native EGL window handler for UWP (external, defined in UWP App)
# endif
//-----------------------------------------------------------------------------------
2018-10-07 01:47:57 +03:00
2016-06-03 01:53:51 +03:00
# if defined(PLATFORM_ANDROID)
2018-04-02 15:49:01 +03:00
static struct android_app * androidApp ; // Android activity
2014-09-17 00:51:31 +04:00
static struct android_poll_source * source ; // Android events polling source
2016-03-21 22:15:11 +03:00
static int ident , events ; // Android ALooper_pollAll() variables
2019-01-05 20:03:09 +03:00
static const char * internalDataPath = NULL ; // Android internal data path to write data (/data/data/<package>/files)
2016-03-21 22:15:11 +03:00
2016-01-03 15:01:21 +03:00
static bool appEnabled = true ; // Used to detec if app is active
static bool contextRebindRequired = false ; // Used to know context rebind required
2016-06-03 01:53:51 +03:00
# endif
2018-10-08 13:29:02 +03:00
// Inputs related variables
//-----------------------------------------------------------------------------------
// Keyboard states
static char previousKeyState [ 512 ] = { 0 } ; // Registers previous frame key state
static char currentKeyState [ 512 ] = { 0 } ; // Registers current frame key state
static int lastKeyPressed = - 1 ; // Register last key pressed
static int exitKey = KEY_ESCAPE ; // Default exit key (ESC)
2014-09-17 00:51:31 +04:00
2018-10-08 13:29:02 +03:00
# if defined(PLATFORM_RPI)
2014-09-17 00:51:31 +04:00
// NOTE: For keyboard we will use the standard input (but reconfigured...)
2016-02-18 16:05:48 +03:00
static struct termios defaultKeyboardSettings ; // Used to store default keyboard settings
2014-09-17 00:51:31 +04:00
static int defaultKeyboardMode ; // Used to store default keyboard mode
2018-10-08 13:29:02 +03:00
# endif
2014-09-17 00:51:31 +04:00
2018-10-08 13:29:02 +03:00
// Mouse states
2019-01-03 15:53:20 +03:00
static Vector2 mousePosition = { 0.0f , 0.0f } ; // Mouse position on screen
static Vector2 mouseScale = { 1.0f , 1.0f } ; // Mouse scaling
static Vector2 mouseOffset = { 0.0f , 0.0f } ; // Mouse offset
2018-10-08 13:29:02 +03:00
static bool cursorHidden = false ; // Track if cursor is hidden
static bool cursorOnScreen = false ; // Tracks if cursor is inside client area
static Vector2 touchPosition [ MAX_TOUCH_POINTS ] ; // Touch position on screen
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
static char previousMouseState [ 3 ] = { 0 } ; // Registers previous mouse button state
static char currentMouseState [ 3 ] = { 0 } ; // Registers current mouse button state
static int previousMouseWheelY = 0 ; // Registers previous mouse wheel variation
static int currentMouseWheelY = 0 ; // Registers current mouse wheel variation
# endif
# if defined(PLATFORM_RPI)
2018-10-21 02:09:17 +03:00
static char currentMouseStateEvdev [ 3 ] = { 0 } ; // Holds the new mouse state for the next polling event to grab (Can't be written directly due to multithreading, app could miss the update)
2018-10-24 14:45:17 +03:00
2018-10-21 02:09:17 +03:00
typedef struct {
2018-10-24 14:45:17 +03:00
pthread_t threadId ; // Event reading thread id
int fd ; // File descriptor to the device it is assigned to
int eventNum ; // Number of 'event<N>' 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 ;
2018-10-21 02:09:17 +03:00
static InputEventWorker eventWorkers [ 10 ] ; // List of worker threads for every monitored "/dev/input/event<N>"
2019-03-28 21:46:39 +03:00
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)
2014-09-17 00:51:31 +04:00
# endif
2018-10-08 13:29:02 +03:00
# if defined(PLATFORM_WEB)
static bool toggleCursorLock = false ; // Ask for cursor pointer lock on next click
2017-11-10 14:37:53 +03:00
# endif
2018-10-08 13:29:02 +03:00
// Gamepads states
static int lastGamepadButtonPressed = - 1 ; // Register last gamepad button pressed
static int gamepadAxisCount = 0 ; // Register number of available gamepad axis
2014-09-17 00:51:31 +04:00
2017-11-10 14:37:53 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
2016-10-14 12:14:41 +03:00
static bool gamepadReady [ MAX_GAMEPADS ] = { false } ; // Flag to know if gamepad is ready
static float gamepadAxisState [ MAX_GAMEPADS ] [ MAX_GAMEPAD_AXIS ] ; // Gamepad axis state
2016-10-29 23:16:54 +03:00
static char previousGamepadState [ MAX_GAMEPADS ] [ MAX_GAMEPAD_BUTTONS ] ; // Previous gamepad buttons state
static char currentGamepadState [ MAX_GAMEPADS ] [ MAX_GAMEPAD_BUTTONS ] ; // Current gamepad buttons state
2016-02-19 21:57:25 +03:00
# endif
2015-07-29 22:44:27 +03:00
2018-10-08 13:29:02 +03:00
# if defined(PLATFORM_RPI)
static int gamepadStream [ MAX_GAMEPADS ] = { - 1 } ; // Gamepad device file descriptor
static pthread_t gamepadThreadId ; // Gamepad reading thread id
static char gamepadName [ 64 ] ; // Gamepad name holder
2015-05-11 01:15:46 +03:00
# endif
2018-10-08 13:29:02 +03:00
//-----------------------------------------------------------------------------------
2015-05-05 00:46:31 +03:00
2018-10-08 13:29:02 +03:00
// Timming system variables
//-----------------------------------------------------------------------------------
2018-04-09 23:28:41 +03:00
static double currentTime = 0.0 ; // Current time measure
static double previousTime = 0.0 ; // Previous time measure
static double updateTime = 0.0 ; // Time measure for frame update
static double drawTime = 0.0 ; // Time measure for frame draw
2017-01-28 02:56:45 +03:00
static double frameTime = 0.0 ; // Time measure for one frame
2014-09-17 00:51:31 +04:00
static double targetTime = 0.0 ; // Desired time for one frame, if 0 not applied
2018-10-08 13:29:02 +03:00
//-----------------------------------------------------------------------------------
2014-01-29 00:21:29 +04:00
2018-10-08 13:29:02 +03:00
// Config internal variables
//-----------------------------------------------------------------------------------
2017-12-24 18:47:33 +03:00
static unsigned char configFlags = 0 ; // Configuration flags (bit based)
2015-07-29 22:44:27 +03:00
static bool showLogo = false ; // Track if showing logo at init is enabled
2014-07-23 02:06:24 +04:00
2018-10-08 13:29:02 +03:00
static char * * dropFilesPath ; // Store dropped files paths as strings
static int dropFilesCount = 0 ; // Count dropped files strings
2018-11-26 19:15:00 +03:00
2018-10-08 13:29:02 +03:00
static char * * dirFilesPath ; // Store directory files paths as strings
static int dirFilesCount = 0 ; // Count directory files strings
2018-04-29 12:49:10 +03:00
# if defined(SUPPORT_SCREEN_CAPTURE)
static int screenshotCounter = 0 ; // Screenshots counter
# endif
2017-05-18 19:57:11 +03:00
# if defined(SUPPORT_GIF_RECORDING)
2018-04-29 12:49:10 +03:00
static int gifFramesCounter = 0 ; // GIF frames counter
static bool gifRecording = false ; // GIF recording state
2017-05-18 19:57:11 +03:00
# endif
2018-10-08 13:29:02 +03:00
//-----------------------------------------------------------------------------------
2017-05-18 19:57:11 +03:00
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by core)
//----------------------------------------------------------------------------------
2017-03-21 15:22:59 +03:00
# if defined(SUPPORT_DEFAULT_FONT)
2016-06-11 15:08:39 +03:00
extern void LoadDefaultFont ( void ) ; // [Module: text] Loads default font on InitWindow()
extern void UnloadDefaultFont ( void ) ; // [Module: text] Unloads default font from GPU memory
2016-10-09 14:09:08 +03:00
# endif
2016-08-06 17:32:46 +03:00
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
2018-02-03 14:12:50 +03:00
static bool InitGraphicsDevice ( int width , int height ) ; // Initialize graphics device
2018-10-08 13:29:02 +03:00
static void SetupFramebuffer ( int width , int height ) ; // Setup main framebuffer
static void SetupViewport ( void ) ; // Set viewport parameters
static void SwapBuffers ( void ) ; // Copy back buffer to front buffers
2014-09-17 00:51:31 +04:00
static void InitTimer ( void ) ; // Initialize timer
2017-03-05 21:17:00 +03:00
static void Wait ( float ms ) ; // Wait for some milliseconds (stop program execution)
2018-10-08 13:29:02 +03:00
2014-09-17 00:51:31 +04:00
static bool GetKeyStatus ( int key ) ; // Returns if a key has been pressed
2017-03-09 18:25:15 +03:00
static bool GetMouseButtonStatus ( int button ) ; // Returns if a mouse button has been pressed
2014-09-17 00:51:31 +04:00
static void PollInputEvents ( void ) ; // Register user events
2018-10-08 13:29:02 +03:00
2014-09-17 00:51:31 +04:00
static void LogoAnimation ( void ) ; // Plays raylib logo appearing animation
2014-12-15 03:08:30 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
2013-11-23 16:30:54 +04:00
static void ErrorCallback ( int error , const char * description ) ; // GLFW3 Error Callback, runs on GLFW3 error
2014-09-17 00:51:31 +04:00
static void KeyCallback ( GLFWwindow * window , int key , int scancode , int action , int mods ) ; // GLFW3 Keyboard Callback, runs on key pressed
2014-12-31 20:03:32 +03:00
static void MouseButtonCallback ( GLFWwindow * window , int button , int action , int mods ) ; // GLFW3 Mouse Button Callback, runs on mouse button pressed
2016-02-02 18:43:42 +03:00
static void MouseCursorPosCallback ( GLFWwindow * window , double x , double y ) ; // GLFW3 Cursor Position Callback, runs on mouse move
2014-12-31 20:03:32 +03:00
static void CharCallback ( GLFWwindow * window , unsigned int key ) ; // GLFW3 Char Key Callback, runs on key pressed (get char value)
2014-09-17 00:51:31 +04:00
static void ScrollCallback ( 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 WindowSizeCallback ( GLFWwindow * window , int width , int height ) ; // GLFW3 WindowSize Callback, runs when window is resized
2015-07-29 22:44:27 +03:00
static void WindowIconifyCallback ( GLFWwindow * window , int iconified ) ; // GLFW3 WindowIconify Callback, runs when window is minimized/restored
static void WindowDropCallback ( GLFWwindow * window , int count , const char * * paths ) ; // GLFW3 Window Drop Callback, runs when drop files into window
2014-12-15 03:08:30 +03:00
# endif
2014-09-17 00:51:31 +04:00
# if defined(PLATFORM_ANDROID)
2015-04-22 18:34:42 +03:00
static void AndroidCommandCallback ( struct android_app * app , int32_t cmd ) ; // Process Android activity lifecycle commands
2016-01-03 15:01:21 +03:00
static int32_t AndroidInputCallback ( struct android_app * app , AInputEvent * event ) ; // Process Android inputs
2014-09-17 00:51:31 +04:00
# endif
2013-11-19 02:38:44 +04:00
2015-10-30 13:30:32 +03:00
# if defined(PLATFORM_WEB)
static EM_BOOL EmscriptenFullscreenChangeCallback ( int eventType , const EmscriptenFullscreenChangeEvent * e , void * userData ) ;
2017-04-08 01:16:03 +03:00
static EM_BOOL EmscriptenKeyboardCallback ( int eventType , const EmscriptenKeyboardEvent * keyEvent , void * userData ) ;
static EM_BOOL EmscriptenMouseCallback ( int eventType , const EmscriptenMouseEvent * mouseEvent , void * userData ) ;
static EM_BOOL EmscriptenTouchCallback ( int eventType , const EmscriptenTouchEvent * touchEvent , void * userData ) ;
2016-12-25 22:42:22 +03:00
static EM_BOOL EmscriptenGamepadCallback ( int eventType , const EmscriptenGamepadEvent * gamepadEvent , void * userData ) ;
2015-10-30 13:30:32 +03:00
# endif
2016-05-02 15:11:57 +03:00
# if defined(PLATFORM_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
2019-03-28 21:46:39 +03:00
static void InitEvdevInput ( void ) ; // Mouse initialization (including mouse thread)
2018-12-18 21:11:12 +03:00
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
2016-05-02 15:11:57 +03:00
static void InitGamepad ( void ) ; // Init raw gamepad input
static void * GamepadThread ( void * arg ) ; // Mouse reading thread
# endif
2017-11-10 14:37:53 +03:00
# if defined(PLATFORM_UWP)
2018-04-02 15:49:01 +03:00
// TODO: Define functions required to manage inputs
2017-11-10 14:37:53 +03:00
# endif
2017-01-28 02:56:45 +03:00
# if defined(_WIN32)
// NOTE: We include Sleep() function signature here to avoid windows.h inclusion
2017-03-05 21:17:00 +03:00
void __stdcall Sleep ( unsigned long msTimeout ) ; // Required for Wait()
2017-01-28 02:56:45 +03:00
# endif
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Module Functions Definition - Window and OpenGL Context Functions
//----------------------------------------------------------------------------------
2018-04-02 15:49:01 +03:00
# if defined(PLATFORM_ANDROID)
2018-11-06 17:10:50 +03:00
// To allow easier porting to android, we allow the user to define a
2018-04-02 15:49:01 +03:00
// 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
androidApp = app ;
// TODO: Should we maybe report != 0 return codes somewhere?
2018-10-18 17:00:11 +03:00
( void ) main ( 1 , ( char * [ ] ) { arg0 , NULL } ) ;
2018-04-02 15:49:01 +03:00
}
// TODO: Add this to header (if apps really need it)
struct android_app * GetAndroidApp ( void )
{
return androidApp ;
}
# endif
2017-05-09 23:03:46 +03:00
// Initialize window and OpenGL context
2017-11-12 13:45:35 +03:00
// NOTE: data parameter could be used to pass any kind of required data to the initialization
2018-04-02 15:49:01 +03:00
void InitWindow ( int width , int height , const char * title )
2013-12-19 15:08:06 +04:00
{
2018-04-10 00:01:20 +03:00
TraceLog ( LOG_INFO , " Initializing raylib %s " , RAYLIB_VERSION ) ;
2015-02-02 02:57:08 +03:00
2018-04-02 15:49:01 +03:00
windowTitle = title ;
# if defined(PLATFORM_ANDROID)
screenWidth = width ;
screenHeight = height ;
2019-02-04 19:10:12 +03:00
currentWidth = width ;
currentHeight = height ;
2017-11-10 14:37:53 +03:00
2018-04-02 15:49:01 +03:00
// Input data is android app pointer
internalDataPath = androidApp - > activity - > internalDataPath ;
// Set desired windows flags before initializing anything
ANativeActivity_setWindowFlags ( androidApp - > activity , AWINDOW_FLAG_FULLSCREEN , 0 ) ; //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
//ANativeActivity_setWindowFlags(androidApp->activity, AWINDOW_FLAG_FORCE_NOT_FULLSCREEN, AWINDOW_FLAG_FULLSCREEN);
int orientation = AConfiguration_getOrientation ( androidApp - > config ) ;
2013-12-19 15:08:06 +04:00
2018-04-02 15:49:01 +03:00
if ( orientation = = ACONFIGURATION_ORIENTATION_PORT ) TraceLog ( LOG_INFO , " PORTRAIT window orientation " ) ;
else if ( orientation = = ACONFIGURATION_ORIENTATION_LAND ) TraceLog ( LOG_INFO , " LANDSCAPE window orientation " ) ;
// TODO: Automatic orientation doesn't seem to work
if ( width < = height )
{
AConfiguration_setOrientation ( androidApp - > config , ACONFIGURATION_ORIENTATION_PORT ) ;
TraceLog ( LOG_WARNING , " Window set to portraid mode " ) ;
}
else
{
AConfiguration_setOrientation ( androidApp - > config , ACONFIGURATION_ORIENTATION_LAND ) ;
TraceLog ( LOG_WARNING , " Window set to landscape mode " ) ;
}
//AConfiguration_getDensity(androidApp->config);
//AConfiguration_getKeyboard(androidApp->config);
//AConfiguration_getScreenSize(androidApp->config);
//AConfiguration_getScreenLong(androidApp->config);
androidApp - > onAppCmd = AndroidCommandCallback ;
androidApp - > onInputEvent = AndroidInputCallback ;
InitAssetManager ( androidApp - > activity - > assetManager ) ;
TraceLog ( LOG_INFO , " Android app initialized successfully " ) ;
// Wait for window to be initialized (display and context)
while ( ! windowReady )
{
// Process events loop
while ( ( ident = ALooper_pollAll ( 0 , NULL , & events , ( void * * ) & source ) ) > = 0 )
{
// Process this event
if ( source ! = NULL ) source - > process ( androidApp , source ) ;
// NOTE: Never close window, native activity is controlled by the system!
//if (androidApp->destroyRequested != 0) windowShouldClose = true;
}
}
# else
2016-06-25 22:28:50 +03:00
// Init graphics device (display device and OpenGL context)
2018-02-04 14:26:28 +03:00
// NOTE: returns true if window and graphic device has been initialized successfully
windowReady = InitGraphicsDevice ( width , height ) ;
2018-02-10 00:50:50 +03:00
if ( ! windowReady ) return ;
2014-09-03 18:51:28 +04:00
2018-03-31 13:22:44 +03:00
// Init hi-res timer
InitTimer ( ) ;
2017-03-21 15:22:59 +03:00
# if defined(SUPPORT_DEFAULT_FONT)
2016-10-09 14:09:08 +03:00
// Load default font
2014-09-17 00:51:31 +04:00
// NOTE: External function (defined in module: text)
LoadDefaultFont ( ) ;
2016-10-09 14:09:08 +03:00
# endif
2014-09-03 18:51:28 +04:00
2014-09-17 00:51:31 +04:00
# if defined(PLATFORM_RPI)
// Init raw input system
2019-03-28 21:46:39 +03:00
InitEvdevInput ( ) ; // Mouse init
2014-09-17 00:51:31 +04:00
InitKeyboard ( ) ; // Keyboard init
InitGamepad ( ) ; // Gamepad init
2014-03-25 15:40:35 +04:00
# endif
2015-04-22 18:34:42 +03:00
2015-10-30 13:30:32 +03:00
# if defined(PLATFORM_WEB)
emscripten_set_fullscreenchange_callback ( 0 , 0 , 1 , EmscriptenFullscreenChangeCallback ) ;
2018-11-06 17:10:50 +03:00
2017-04-08 01:16:03 +03:00
// Support keyboard events
emscripten_set_keypress_callback ( " #canvas " , NULL , 1 , EmscriptenKeyboardCallback ) ;
2018-11-06 17:10:50 +03:00
2017-04-08 01:16:03 +03:00
// 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 ) ;
2016-01-03 15:01:21 +03:00
//emscripten_set_touchstart_callback(0, NULL, 1, Emscripten_HandleTouch);
//emscripten_set_touchend_callback("#canvas", data, 0, Emscripten_HandleTouch);
2016-08-16 12:09:55 +03:00
2017-04-08 01:16:03 +03:00
// Support gamepad events (not provided by GLFW3 on emscripten)
2016-12-25 22:42:22 +03:00
emscripten_set_gamepadconnected_callback ( NULL , 1 , EmscriptenGamepadCallback ) ;
emscripten_set_gamepaddisconnected_callback ( NULL , 1 , EmscriptenGamepadCallback ) ;
2015-10-30 13:30:32 +03:00
# endif
2016-01-13 21:30:35 +03:00
mousePosition . x = ( float ) screenWidth / 2.0f ;
mousePosition . y = ( float ) screenHeight / 2.0f ;
2014-03-25 15:40:35 +04:00
2014-09-17 00:51:31 +04:00
// raylib logo appearing animation (if enabled)
if ( showLogo )
2013-11-19 02:38:44 +04:00
{
2014-09-17 00:51:31 +04:00
SetTargetFPS ( 60 ) ;
LogoAnimation ( ) ;
2013-11-19 02:38:44 +04:00
}
2018-04-02 15:49:01 +03:00
# endif // defined(PLATFORM_ANDROID)
2014-09-17 00:51:31 +04:00
}
2013-11-19 02:38:44 +04:00
2017-05-09 23:03:46 +03:00
// Close window and unload OpenGL context
2014-09-03 19:06:10 +04:00
void CloseWindow ( void )
2013-11-19 02:38:44 +04:00
{
2017-05-18 19:57:11 +03:00
# if defined(SUPPORT_GIF_RECORDING)
if ( gifRecording )
{
2017-05-27 15:40:05 +03:00
GifEnd ( ) ;
2017-05-18 19:57:11 +03:00
gifRecording = false ;
}
# endif
2018-11-06 17:10:50 +03:00
2017-03-21 15:22:59 +03:00
# if defined(SUPPORT_DEFAULT_FONT)
2013-11-23 16:30:54 +04:00
UnloadDefaultFont ( ) ;
2016-10-09 14:09:08 +03:00
# endif
2014-09-03 18:51:28 +04:00
2014-09-17 00:51:31 +04:00
rlglClose ( ) ; // De-init rlgl
2013-11-19 02:38:44 +04:00
2014-12-15 03:08:30 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
2013-11-19 02:38:44 +04:00
glfwDestroyWindow ( window ) ;
2013-11-23 16:30:54 +04:00
glfwTerminate ( ) ;
2016-06-03 01:53:51 +03:00
# endif
2017-05-08 03:37:37 +03:00
# if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32)
timeEndPeriod ( 1 ) ; // Restore time period
# endif
2017-11-10 14:37:53 +03:00
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
2014-09-17 00:51:31 +04:00
// Close surface, context and display
if ( display ! = EGL_NO_DISPLAY )
{
eglMakeCurrent ( display , EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT ) ;
if ( surface ! = EGL_NO_SURFACE )
{
eglDestroySurface ( display , surface ) ;
surface = EGL_NO_SURFACE ;
}
if ( context ! = EGL_NO_CONTEXT )
{
eglDestroyContext ( display , context ) ;
context = EGL_NO_CONTEXT ;
}
eglTerminate ( display ) ;
display = EGL_NO_DISPLAY ;
2016-08-16 12:09:55 +03:00
}
2016-06-24 20:34:47 +03:00
# endif
# if defined(PLATFORM_RPI)
// 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 windowShouldClose variable
2017-01-29 01:02:30 +03:00
2016-10-24 20:08:23 +03:00
windowShouldClose = true ; // Added to force threads to exit when the close window is called
2017-01-29 01:02:30 +03:00
2018-10-21 02:09:17 +03:00
for ( int i = 0 ; i < sizeof ( eventWorkers ) / sizeof ( InputEventWorker ) ; + + i )
{
2018-10-22 12:48:16 +03:00
if ( eventWorkers [ i ] . threadId = = 0 )
2018-10-21 02:09:17 +03:00
{
pthread_join ( eventWorkers [ i ] . threadId , NULL ) ;
}
}
2016-06-24 20:34:47 +03:00
pthread_join ( gamepadThreadId , NULL ) ;
2014-09-17 00:51:31 +04:00
# endif
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Window closed successfully " ) ;
2013-11-19 02:38:44 +04:00
}
2018-02-04 14:26:28 +03:00
// Check if window has been initialized successfully
bool IsWindowReady ( void )
{
return windowReady ;
}
2017-05-09 23:03:46 +03:00
// Check if KEY_ESCAPE pressed or Close icon pressed
2014-09-17 00:51:31 +04:00
bool WindowShouldClose ( void )
2015-02-02 02:57:08 +03:00
{
2018-07-21 18:13:59 +03:00
# if defined(PLATFORM_WEB)
// Emterpreter-Async required to run sync code
2019-02-21 20:45:19 +03:00
// https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
2018-07-21 18:13:59 +03:00
// 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
2018-11-06 17:10:50 +03:00
2018-07-21 18:13:59 +03:00
# if defined(PLATFORM_DESKTOP)
2018-02-04 14:31:16 +03:00
if ( windowReady )
{
// While window minimized, stop loop execution
while ( windowMinimized ) glfwWaitEvents ( ) ;
2015-01-21 02:13:17 +03:00
2018-02-04 14:31:16 +03:00
return ( glfwWindowShouldClose ( window ) ) ;
}
else return true ;
2016-06-03 01:53:51 +03:00
# endif
2017-11-10 14:37:53 +03:00
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
2018-02-04 14:31:16 +03:00
if ( windowReady ) return windowShouldClose ;
else return true ;
2014-09-17 00:51:31 +04:00
# endif
}
2017-05-09 23:03:46 +03:00
// Check if window has been minimized (or lost focus)
2015-07-29 22:44:27 +03:00
bool IsWindowMinimized ( void )
{
2017-11-10 14:37:53 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
2015-07-29 22:44:27 +03:00
return windowMinimized ;
# else
return false ;
# endif
}
2019-03-05 00:58:20 +03:00
// Check if window has been resized
bool IsWindowResized ( void )
{
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_UWP)
return windowResized ;
# else
return false ;
2019-04-04 14:50:52 +03:00
# endif
2019-03-05 00:58:20 +03:00
}
2019-01-10 19:06:26 +03:00
// Check if window is currently hidden
bool IsWindowHidden ( void )
{
# if defined(PLATFORM_DESKTOP)
return ( glfwGetWindowAttrib ( window , GLFW_VISIBLE ) = = GL_FALSE ) ;
# endif
return false ;
}
2017-05-09 23:03:46 +03:00
// Toggle fullscreen mode (only PLATFORM_DESKTOP)
2014-09-17 00:51:31 +04:00
void ToggleFullscreen ( void )
{
# if defined(PLATFORM_DESKTOP)
fullscreen = ! fullscreen ; // Toggle fullscreen flag
2016-06-26 02:36:06 +03:00
// NOTE: glfwSetWindowMonitor() doesn't work properly (bugs)
if ( fullscreen ) glfwSetWindowMonitor ( window , glfwGetPrimaryMonitor ( ) , 0 , 0 , screenWidth , screenHeight , GLFW_DONT_CARE ) ;
else glfwSetWindowMonitor ( window , NULL , 0 , 0 , screenWidth , screenHeight , GLFW_DONT_CARE ) ;
2016-06-03 01:53:51 +03:00
# endif
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_WARNING , " Could not toggle to windowed mode " ) ;
2014-09-17 00:51:31 +04:00
# endif
}
2017-02-05 05:00:35 +03:00
// Set icon for window (only PLATFORM_DESKTOP)
2019-03-16 15:00:46 +03:00
// NOTE: Image must be in RGBA format, 8bit per channel
2017-02-05 05:00:35 +03:00
void SetWindowIcon ( Image image )
{
# if defined(PLATFORM_DESKTOP)
2019-03-16 15:00:46 +03:00
if ( image . format = = UNCOMPRESSED_R8G8B8A8 )
{
GLFWimage icon [ 1 ] = { 0 } ;
2017-02-05 05:00:35 +03:00
2019-03-16 15:00:46 +03:00
icon [ 0 ] . width = image . width ;
icon [ 0 ] . height = image . height ;
icon [ 0 ] . pixels = ( unsigned char * ) image . data ;
2017-02-05 05:00:35 +03:00
2019-03-16 15:00:46 +03:00
// 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 " ) ;
2017-02-05 05:00:35 +03:00
# endif
}
2017-09-08 10:35:54 +03:00
// Set title for window (only PLATFORM_DESKTOP)
void SetWindowTitle ( const char * title )
{
# if defined(PLATFORM_DESKTOP)
glfwSetWindowTitle ( window , title ) ;
# endif
}
2017-03-05 12:55:29 +03:00
// Set window position on screen (windowed mode)
void SetWindowPosition ( int x , int y )
{
2017-03-09 15:13:13 +03:00
# if defined(PLATFORM_DESKTOP)
2017-03-05 12:55:29 +03:00
glfwSetWindowPos ( window , x , y ) ;
2017-03-09 15:13:13 +03:00
# endif
2017-03-05 12:55:29 +03:00
}
// Set monitor for the current window (fullscreen mode)
void SetWindowMonitor ( int monitor )
{
2017-03-09 15:13:13 +03:00
# if defined(PLATFORM_DESKTOP)
2017-03-05 12:55:29 +03:00
int monitorCount ;
2018-10-22 12:48:16 +03:00
GLFWmonitor * * monitors = glfwGetMonitors ( & monitorCount ) ;
2018-11-06 17:10:50 +03:00
if ( ( monitor > = 0 ) & & ( monitor < monitorCount ) )
2017-03-05 12:55:29 +03:00
{
2017-11-10 14:37:53 +03:00
//glfwSetWindowMonitor(window, monitors[monitor], 0, 0, screenWidth, screenHeight, GLFW_DONT_CARE);
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Selected fullscreen monitor: [%i] %s " , monitor , glfwGetMonitorName ( monitors [ monitor ] ) ) ;
2017-03-05 12:55:29 +03:00
}
2017-07-02 13:35:13 +03:00
else TraceLog ( LOG_WARNING , " Selected monitor not found " ) ;
2017-03-09 15:13:13 +03:00
# endif
2017-03-05 12:55:29 +03:00
}
2017-05-09 23:03:46 +03:00
// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
2017-05-02 16:04:32 +03:00
void SetWindowMinSize ( int width , int height )
{
# if defined(PLATFORM_DESKTOP)
const GLFWvidmode * mode = glfwGetVideoMode ( glfwGetPrimaryMonitor ( ) ) ;
glfwSetWindowSizeLimits ( window , width , height , mode - > width , mode - > height ) ;
# endif
}
2018-03-09 13:43:53 +03:00
// Set window dimensions
void SetWindowSize ( int width , int height )
{
# if defined(PLATFORM_DESKTOP)
glfwSetWindowSize ( window , width , height ) ;
# endif
}
2019-01-10 18:43:21 +03:00
// Show the window
2019-01-17 18:29:36 +03:00
void UnhideWindow ( void )
2019-01-10 16:55:19 +03:00
{
# if defined(PLATFORM_DESKTOP)
2019-01-10 18:43:21 +03:00
glfwShowWindow ( window ) ;
2019-01-10 16:55:19 +03:00
# endif
}
2019-01-10 18:43:21 +03:00
// Hide the window
2019-01-10 19:06:26 +03:00
void HideWindow ( void )
2019-01-10 16:55:19 +03:00
{
# if defined(PLATFORM_DESKTOP)
2019-01-10 18:43:21 +03:00
glfwHideWindow ( window ) ;
2019-01-10 16:55:19 +03:00
# endif
2019-01-10 18:43:21 +03:00
}
2014-09-17 00:51:31 +04:00
// Get current screen width
int GetScreenWidth ( void )
2013-11-19 02:38:44 +04:00
{
2014-09-17 00:51:31 +04:00
return screenWidth ;
2013-11-19 02:38:44 +04:00
}
2014-09-17 00:51:31 +04:00
// Get current screen height
int GetScreenHeight ( void )
2013-11-19 02:38:44 +04:00
{
2014-09-17 00:51:31 +04:00
return screenHeight ;
2013-11-19 02:38:44 +04:00
}
2018-10-08 13:29:02 +03:00
// Get native window handle
void * GetWindowHandle ( void )
{
# if defined(_WIN32)
// NOTE: Returned handle is: void *HWND (windows.h)
2019-02-22 14:12:21 +03:00
return glfwGetWin32Window ( window ) ;
2018-10-08 13:29:02 +03:00
# elif defined(__linux__)
// NOTE: Returned handle is: unsigned long Window (X.h)
// typedef unsigned long XID;
// typedef XID Window;
2018-10-08 14:14:15 +03:00
//unsigned long id = (unsigned long)glfwGetX11Window(window);
2018-10-08 13:29:02 +03:00
return NULL ; // TODO: Find a way to return value... cast to void *?
# elif defined(__APPLE__)
2018-10-08 19:08:39 +03:00
// NOTE: Returned handle is: (objc_object *)
2018-10-08 19:51:41 +03:00
return NULL ; // TODO: return (void *)glfwGetCocoaWindow(window);
2018-10-08 13:29:02 +03:00
# else
return NULL ;
# endif
}
2018-09-27 17:52:56 +03:00
// Get number of monitors
int GetMonitorCount ( void )
{
2018-09-29 16:10:29 +03:00
# if defined(PLATFORM_DESKTOP)
2018-09-27 18:23:11 +03:00
int monitorCount ;
glfwGetMonitors ( & monitorCount ) ;
return monitorCount ;
2018-09-29 16:28:07 +03:00
# else
2018-09-29 16:10:29 +03:00
return 1 ;
2018-09-29 16:28:07 +03:00
# endif
2018-09-27 17:52:56 +03:00
}
// Get primary monitor width
2018-09-30 17:20:02 +03:00
int GetMonitorWidth ( int monitor )
2018-11-06 17:10:50 +03:00
{
2018-09-29 16:10:29 +03:00
# if defined(PLATFORM_DESKTOP)
2018-09-30 17:20:02 +03:00
int monitorCount ;
2018-10-22 12:48:16 +03:00
GLFWmonitor * * monitors = glfwGetMonitors ( & monitorCount ) ;
2018-11-06 17:10:50 +03:00
if ( ( monitor > = 0 ) & & ( monitor < monitorCount ) )
2018-09-30 17:20:02 +03:00
{
const GLFWvidmode * mode = glfwGetVideoMode ( monitors [ monitor ] ) ;
return mode - > width ;
}
else TraceLog ( LOG_WARNING , " Selected monitor not found " ) ;
2018-09-29 16:28:07 +03:00
# endif
2018-09-30 17:20:02 +03:00
return 0 ;
2018-09-27 17:52:56 +03:00
}
2018-09-30 17:20:02 +03:00
// Get primary monitor width
int GetMonitorHeight ( int monitor )
2018-11-06 17:10:50 +03:00
{
2018-09-29 16:10:29 +03:00
# if defined(PLATFORM_DESKTOP)
2018-09-30 17:20:02 +03:00
int monitorCount ;
2018-10-22 12:48:16 +03:00
GLFWmonitor * * monitors = glfwGetMonitors ( & monitorCount ) ;
2018-11-06 17:10:50 +03:00
if ( ( monitor > = 0 ) & & ( monitor < monitorCount ) )
2018-09-30 17:20:02 +03:00
{
const GLFWvidmode * mode = glfwGetVideoMode ( monitors [ monitor ] ) ;
return mode - > height ;
}
else TraceLog ( LOG_WARNING , " Selected monitor not found " ) ;
2018-09-29 16:28:07 +03:00
# endif
2018-09-30 17:20:02 +03:00
return 0 ;
2018-09-27 17:52:56 +03:00
}
// Get primary montior physical width in millimetres
2018-09-30 17:20:02 +03:00
int GetMonitorPhysicalWidth ( int monitor )
2018-09-27 17:52:56 +03:00
{
2018-09-29 16:10:29 +03:00
# if defined(PLATFORM_DESKTOP)
2018-09-30 17:20:02 +03:00
int monitorCount ;
2018-10-22 12:48:16 +03:00
GLFWmonitor * * monitors = glfwGetMonitors ( & monitorCount ) ;
2018-11-06 17:10:50 +03:00
if ( ( monitor > = 0 ) & & ( monitor < monitorCount ) )
2018-09-30 17:20:02 +03:00
{
int physicalWidth ;
glfwGetMonitorPhysicalSize ( monitors [ monitor ] , & physicalWidth , NULL ) ;
return physicalWidth ;
}
else TraceLog ( LOG_WARNING , " Selected monitor not found " ) ;
2018-09-29 16:28:07 +03:00
# endif
2018-09-30 17:20:02 +03:00
return 0 ;
2018-09-27 17:52:56 +03:00
}
// Get primary monitor physical height in millimetres
2018-09-30 17:20:02 +03:00
int GetMonitorPhysicalHeight ( int monitor )
2018-09-27 17:52:56 +03:00
{
2018-09-30 17:20:02 +03:00
# if defined(PLATFORM_DESKTOP)
int monitorCount ;
2018-10-22 12:48:16 +03:00
GLFWmonitor * * monitors = glfwGetMonitors ( & monitorCount ) ;
2018-11-06 17:10:50 +03:00
if ( ( monitor > = 0 ) & & ( monitor < monitorCount ) )
2018-09-30 17:20:02 +03:00
{
int physicalHeight ;
glfwGetMonitorPhysicalSize ( monitors [ monitor ] , NULL , & physicalHeight ) ;
return physicalHeight ;
}
else TraceLog ( LOG_WARNING , " Selected monitor not found " ) ;
2018-09-29 16:28:07 +03:00
# endif
2018-09-30 17:20:02 +03:00
return 0 ;
2018-09-27 17:52:56 +03:00
}
// Get the human-readable, UTF-8 encoded name of the primary monitor
2018-09-30 17:20:02 +03:00
const char * GetMonitorName ( int monitor )
2018-09-27 17:52:56 +03:00
{
2018-11-06 17:10:50 +03:00
# if defined(PLATFORM_DESKTOP)
2018-09-30 17:20:02 +03:00
int monitorCount ;
2018-10-22 12:48:16 +03:00
GLFWmonitor * * monitors = glfwGetMonitors ( & monitorCount ) ;
2018-11-06 17:10:50 +03:00
if ( ( monitor > = 0 ) & & ( monitor < monitorCount ) )
2018-09-30 17:20:02 +03:00
{
return glfwGetMonitorName ( monitors [ monitor ] ) ;
}
else TraceLog ( LOG_WARNING , " Selected monitor not found " ) ;
2018-09-29 16:28:07 +03:00
# endif
2018-09-30 17:20:02 +03:00
return " " ;
2018-09-27 17:52:56 +03:00
}
2019-02-11 20:03:06 +03:00
// Get clipboard text content
// NOTE: returned string is allocated and freed by GLFW
const char * GetClipboardText ( void )
{
# if defined(PLATFORM_DESKTOP)
return glfwGetClipboardString ( window ) ;
2019-03-01 00:25:27 +03:00
# else
return NULL ;
2019-02-11 20:03:06 +03:00
# endif
}
// Set clipboard text content
void SetClipboardText ( const char * text )
{
# if defined(PLATFORM_DESKTOP)
glfwSetClipboardString ( window , text ) ;
# endif
}
2016-10-17 19:18:13 +03:00
// Show mouse cursor
2019-01-14 15:49:56 +03:00
void ShowCursor ( void )
2016-10-17 19:18:13 +03:00
{
# if defined(PLATFORM_DESKTOP)
2018-03-03 18:01:24 +03:00
glfwSetInputMode ( window , GLFW_CURSOR , GLFW_CURSOR_NORMAL ) ;
2016-10-17 19:18:13 +03:00
# endif
cursorHidden = false ;
}
2017-05-09 23:03:46 +03:00
// Hides mouse cursor
2019-01-14 15:49:56 +03:00
void HideCursor ( void )
2016-10-17 19:18:13 +03:00
{
# if defined(PLATFORM_DESKTOP)
2018-03-03 18:01:24 +03:00
glfwSetInputMode ( window , GLFW_CURSOR , GLFW_CURSOR_HIDDEN ) ;
2016-10-17 19:18:13 +03:00
# endif
cursorHidden = true ;
}
2017-05-09 23:03:46 +03:00
// Check if cursor is not visible
2019-01-14 15:49:56 +03:00
bool IsCursorHidden ( void )
2016-10-17 19:18:13 +03:00
{
return cursorHidden ;
}
2017-05-09 23:03:46 +03:00
// Enables cursor (unlock cursor)
2019-01-14 15:49:56 +03:00
void EnableCursor ( void )
2016-10-17 19:18:13 +03:00
{
# if defined(PLATFORM_DESKTOP)
glfwSetInputMode ( window , GLFW_CURSOR , GLFW_CURSOR_NORMAL ) ;
2017-05-08 22:03:48 +03:00
# endif
# if defined(PLATFORM_WEB)
toggleCursorLock = true ;
2016-10-17 19:18:13 +03:00
# endif
cursorHidden = false ;
}
2017-05-09 23:03:46 +03:00
// Disables cursor (lock cursor)
2019-01-14 15:49:56 +03:00
void DisableCursor ( void )
2016-10-17 19:18:13 +03:00
{
# if defined(PLATFORM_DESKTOP)
glfwSetInputMode ( window , GLFW_CURSOR , GLFW_CURSOR_DISABLED ) ;
2017-05-08 22:03:48 +03:00
# endif
# if defined(PLATFORM_WEB)
toggleCursorLock = true ;
2016-10-17 19:18:13 +03:00
# endif
cursorHidden = true ;
}
2017-05-09 23:03:46 +03:00
// Set background color (framebuffer clear color)
2013-11-19 02:38:44 +04:00
void ClearBackground ( Color color )
{
2018-01-06 04:43:38 +03:00
rlClearColor ( color . r , color . g , color . b , color . a ) ; // Set clear color
rlClearScreenBuffers ( ) ; // Clear current framebuffers
2013-11-19 02:38:44 +04:00
}
2017-05-09 23:03:46 +03:00
// Setup canvas (framebuffer) to start drawing
2014-09-03 19:06:10 +04:00
void BeginDrawing ( void )
2013-11-19 02:38:44 +04:00
{
2017-12-19 16:06:54 +03:00
currentTime = GetTime ( ) ; // Number of elapsed seconds since InitTimer()
2013-11-23 16:30:54 +04:00
updateTime = currentTime - previousTime ;
previousTime = currentTime ;
2016-08-16 12:09:55 +03:00
2014-03-25 15:40:35 +04:00
rlLoadIdentity ( ) ; // Reset current matrix (MODELVIEW)
2016-01-11 15:29:55 +03:00
rlMultMatrixf ( MatrixToFloat ( downscaleView ) ) ; // If downscale required, apply it here
2014-09-17 00:51:31 +04:00
2015-02-02 02:57:08 +03:00
//rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
2014-03-25 15:40:35 +04:00
// NOTE: Not required with OpenGL 3.3+
2013-11-19 02:38:44 +04:00
}
2017-05-09 23:03:46 +03:00
// End canvas drawing and swap buffers (double buffering)
2014-09-03 19:06:10 +04:00
void EndDrawing ( void )
2013-11-19 02:38:44 +04:00
{
2016-06-14 18:16:20 +03:00
rlglDraw ( ) ; // Draw Buffers (Only OpenGL 3+ and ES2)
2016-06-03 01:53:51 +03:00
2017-05-18 19:57:11 +03:00
# if defined(SUPPORT_GIF_RECORDING)
# define GIF_RECORD_FRAMERATE 10
if ( gifRecording )
{
gifFramesCounter + + ;
2018-11-06 17:10:50 +03:00
2017-05-18 19:57:11 +03:00
// NOTE: We record one gif frame every 10 game frames
if ( ( gifFramesCounter % GIF_RECORD_FRAMERATE ) = = 0 )
{
// Get image data for the current frame (from backbuffer)
// NOTE: This process is very slow... :(
2017-07-17 01:33:40 +03:00
unsigned char * screenData = rlReadScreenPixels ( screenWidth , screenHeight ) ;
2017-05-27 15:40:05 +03:00
GifWriteFrame ( screenData , screenWidth , screenHeight , 10 , 8 , false ) ;
2018-11-06 17:10:50 +03:00
2019-04-23 15:55:35 +03:00
RL_FREE ( screenData ) ; // Free image data
2017-05-18 19:57:11 +03:00
}
2018-11-06 17:10:50 +03:00
2017-05-18 19:57:11 +03:00
if ( ( ( gifFramesCounter / 15 ) % 2 ) = = 1 )
{
DrawCircle ( 30 , screenHeight - 20 , 10 , RED ) ;
DrawText ( " RECORDING " , 50 , screenHeight - 25 , 10 , MAROON ) ;
}
2018-11-06 17:10:50 +03:00
2017-05-18 19:57:11 +03:00
rlglDraw ( ) ; // Draw RECORDING message
}
# endif
2018-11-06 17:10:50 +03:00
2016-06-03 01:53:51 +03:00
SwapBuffers ( ) ; // Copy back buffer to front buffer
2014-09-17 00:51:31 +04:00
PollInputEvents ( ) ; // Poll user events
2016-08-16 12:09:55 +03:00
2016-04-01 11:39:33 +03:00
// Frame time control system
2014-09-17 00:51:31 +04:00
currentTime = GetTime ( ) ;
2013-11-23 16:30:54 +04:00
drawTime = currentTime - previousTime ;
previousTime = currentTime ;
2018-11-06 17:10:50 +03:00
2013-11-23 16:30:54 +04:00
frameTime = updateTime + drawTime ;
2014-09-03 18:51:28 +04:00
2017-01-28 02:56:45 +03:00
// Wait for some milliseconds...
2017-01-29 01:02:30 +03:00
if ( frameTime < targetTime )
2013-11-23 16:30:54 +04:00
{
2018-09-14 13:47:31 +03:00
Wait ( ( float ) ( targetTime - frameTime ) * 1000.0f ) ;
2017-01-29 01:02:30 +03:00
2014-09-17 00:51:31 +04:00
currentTime = GetTime ( ) ;
2017-01-28 02:56:45 +03:00
double extraTime = currentTime - previousTime ;
2013-11-23 16:30:54 +04:00
previousTime = currentTime ;
2017-01-29 01:02:30 +03:00
2017-03-05 21:17:00 +03:00
frameTime + = extraTime ;
2013-11-23 16:30:54 +04:00
}
2013-11-19 02:38:44 +04:00
}
2017-05-09 23:03:46 +03:00
// Initialize 2D mode with custom camera (2D)
2018-05-04 17:54:05 +03:00
void BeginMode2D ( Camera2D camera )
2016-05-02 01:37:33 +03:00
{
rlglDraw ( ) ; // Draw Buffers (Only OpenGL 3+ and ES2)
rlLoadIdentity ( ) ; // Reset current matrix (MODELVIEW)
// Camera rotation and scaling is always relative to target
Matrix matOrigin = MatrixTranslate ( - camera . target . x , - camera . target . y , 0.0f ) ;
Matrix matRotation = MatrixRotate ( ( Vector3 ) { 0.0f , 0.0f , 1.0f } , camera . rotation * DEG2RAD ) ;
Matrix matScale = MatrixScale ( camera . zoom , camera . zoom , 1.0f ) ;
Matrix matTranslation = MatrixTranslate ( camera . offset . x + camera . target . x , camera . offset . y + camera . target . y , 0.0f ) ;
2016-08-16 12:09:55 +03:00
2016-05-02 01:37:33 +03:00
Matrix matTransform = MatrixMultiply ( MatrixMultiply ( matOrigin , MatrixMultiply ( matScale , matRotation ) ) , matTranslation ) ;
2016-08-16 12:09:55 +03:00
2019-03-29 14:23:02 +03:00
rlMultMatrixf ( MatrixToFloat ( matTransform ) ) ; // Apply transformation to modelview
2016-05-02 01:37:33 +03:00
}
2017-05-09 23:03:46 +03:00
// Ends 2D mode with custom camera
2018-05-04 17:54:05 +03:00
void EndMode2D ( void )
2016-05-02 01:37:33 +03:00
{
rlglDraw ( ) ; // Draw Buffers (Only OpenGL 3+ and ES2)
rlLoadIdentity ( ) ; // Reset current matrix (MODELVIEW)
}
2017-05-09 23:03:46 +03:00
// Initializes 3D mode with custom camera (3D)
2018-05-04 17:54:05 +03:00
void BeginMode3D ( Camera3D camera )
2013-11-19 02:38:44 +04:00
{
2015-02-02 02:57:08 +03:00
rlglDraw ( ) ; // Draw Buffers (Only OpenGL 3+ and ES2)
2018-11-06 17:10:50 +03:00
2014-03-25 15:40:35 +04:00
rlMatrixMode ( RL_PROJECTION ) ; // Switch to projection matrix
rlPushMatrix ( ) ; // Save previous matrix, which contains the settings for the 2d ortho projection
rlLoadIdentity ( ) ; // Reset current matrix (PROJECTION)
2016-08-16 12:09:55 +03:00
2019-02-04 19:10:12 +03:00
float aspect = ( float ) currentWidth / ( float ) currentHeight ;
2018-03-27 20:59:54 +03:00
2018-11-06 17:10:50 +03:00
if ( camera . type = = CAMERA_PERSPECTIVE )
2018-03-25 01:31:06 +03:00
{
// Setup perspective projection
double top = 0.01 * tan ( camera . fovy * 0.5 * DEG2RAD ) ;
double right = top * aspect ;
rlFrustum ( - right , right , - top , top , 0.01 , 1000.0 ) ;
}
2018-04-19 21:19:53 +03:00
else if ( camera . type = = CAMERA_ORTHOGRAPHIC )
2018-03-25 01:31:06 +03:00
{
// Setup orthographic projection
double top = camera . fovy / 2.0 ;
double right = top * aspect ;
rlOrtho ( - right , right , - top , top , 0.01 , 1000.0 ) ;
}
2013-11-23 16:30:54 +04:00
2015-08-27 17:10:15 +03:00
// NOTE: zNear and zFar values are important when computing depth buffer values
2014-09-03 18:51:28 +04:00
2014-03-25 15:40:35 +04:00
rlMatrixMode ( RL_MODELVIEW ) ; // Switch back to modelview matrix
rlLoadIdentity ( ) ; // Reset current matrix (MODELVIEW)
2014-09-03 18:51:28 +04:00
2014-03-25 15:40:35 +04:00
// Setup Camera view
2017-07-21 18:19:28 +03:00
Matrix matView = MatrixLookAt ( camera . position , camera . target , camera . up ) ;
rlMultMatrixf ( MatrixToFloat ( matView ) ) ; // Multiply MODELVIEW matrix by view matrix (camera)
2016-08-16 12:09:55 +03:00
2016-03-17 15:51:48 +03:00
rlEnableDepthTest ( ) ; // Enable DEPTH_TEST for 3D
2013-11-19 02:38:44 +04:00
}
// Ends 3D mode and returns to default 2D orthographic mode
2018-05-04 17:54:05 +03:00
void EndMode3D ( void )
2016-08-16 12:09:55 +03:00
{
2016-06-21 14:49:13 +03:00
rlglDraw ( ) ; // Process internal buffers (update + draw)
2016-08-16 12:09:55 +03:00
2014-03-25 15:40:35 +04:00
rlMatrixMode ( RL_PROJECTION ) ; // Switch to projection matrix
rlPopMatrix ( ) ; // Restore previous matrix (PROJECTION) from matrix stack
2014-09-03 18:51:28 +04:00
2014-03-25 15:40:35 +04:00
rlMatrixMode ( RL_MODELVIEW ) ; // Get back to modelview matrix
rlLoadIdentity ( ) ; // Reset current matrix (MODELVIEW)
2014-09-03 18:51:28 +04:00
2016-03-17 15:51:48 +03:00
rlDisableDepthTest ( ) ; // Disable DEPTH_TEST for 2D
2013-11-19 02:38:44 +04:00
}
2016-03-30 21:09:16 +03:00
// Initializes render texture for drawing
void BeginTextureMode ( RenderTexture2D target )
{
rlglDraw ( ) ; // Draw Buffers (Only OpenGL 3+ and ES2)
2016-05-29 12:49:13 +03:00
rlEnableRenderTexture ( target . id ) ; // Enable render target
2018-11-06 17:10:50 +03:00
2016-05-29 12:49:13 +03:00
// Set viewport to framebuffer size
2016-08-16 12:09:55 +03:00
rlViewport ( 0 , 0 , target . texture . width , target . texture . height ) ;
2016-05-29 12:49:13 +03:00
rlMatrixMode ( RL_PROJECTION ) ; // Switch to PROJECTION matrix
rlLoadIdentity ( ) ; // Reset current matrix (PROJECTION)
2016-03-30 21:09:16 +03:00
2016-05-29 12:49:13 +03:00
// Set orthographic projection to current framebuffer size
// NOTE: Configured top-left corner as (0, 0)
2016-08-16 12:09:55 +03:00
rlOrtho ( 0 , target . texture . width , target . texture . height , 0 , 0.0f , 1.0f ) ;
2016-05-29 12:49:13 +03:00
rlMatrixMode ( RL_MODELVIEW ) ; // Switch back to MODELVIEW matrix
2016-03-30 21:09:16 +03:00
rlLoadIdentity ( ) ; // Reset current matrix (MODELVIEW)
2016-05-29 12:49:13 +03:00
//rlScalef(0.0f, -1.0f, 0.0f); // Flip Y-drawing (?)
2019-02-21 20:45:19 +03:00
2019-02-04 19:10:12 +03:00
// Setup current width/height for proper aspect ratio
// calculation when using BeginMode3D()
currentWidth = target . texture . width ;
currentHeight = target . texture . height ;
2016-03-30 21:09:16 +03:00
}
// Ends drawing to render texture
void EndTextureMode ( void )
{
rlglDraw ( ) ; // Draw Buffers (Only OpenGL 3+ and ES2)
2016-05-29 12:49:13 +03:00
rlDisableRenderTexture ( ) ; // Disable render target
// Set viewport to default framebuffer size (screen size)
2016-12-15 10:58:15 +03:00
SetupViewport ( ) ;
2016-08-16 12:09:55 +03:00
2016-05-29 12:49:13 +03:00
rlMatrixMode ( RL_PROJECTION ) ; // Switch to PROJECTION matrix
rlLoadIdentity ( ) ; // Reset current matrix (PROJECTION)
2016-08-16 12:09:55 +03:00
2016-05-29 12:49:13 +03:00
// Set orthographic projection to current framebuffer size
// NOTE: Configured top-left corner as (0, 0)
rlOrtho ( 0 , GetScreenWidth ( ) , GetScreenHeight ( ) , 0 , 0.0f , 1.0f ) ;
rlMatrixMode ( RL_MODELVIEW ) ; // Switch back to MODELVIEW matrix
rlLoadIdentity ( ) ; // Reset current matrix (MODELVIEW)
2019-02-21 20:45:19 +03:00
2019-02-13 19:49:02 +03:00
// Reset current screen size
currentWidth = GetScreenWidth ( ) ;
currentHeight = GetScreenHeight ( ) ;
2016-03-30 21:09:16 +03:00
}
2017-05-09 23:03:46 +03:00
// Returns a ray trace from mouse position
Ray GetMouseRay ( Vector2 mousePosition , Camera camera )
{
Ray ray ;
// Calculate normalized device coordinates
// NOTE: y value is negative
float x = ( 2.0f * mousePosition . x ) / ( float ) GetScreenWidth ( ) - 1.0f ;
float y = 1.0f - ( 2.0f * mousePosition . y ) / ( float ) GetScreenHeight ( ) ;
float z = 1.0f ;
// Store values in a vector
Vector3 deviceCoords = { x , y , z } ;
// Calculate view matrix from camera look at
Matrix matView = MatrixLookAt ( camera . position , camera . target , camera . up ) ;
2018-05-17 01:58:58 +03:00
Matrix matProj = MatrixIdentity ( ) ;
2018-03-25 01:31:06 +03:00
2018-11-06 17:10:50 +03:00
if ( camera . type = = CAMERA_PERSPECTIVE )
2018-03-25 01:31:06 +03:00
{
// Calculate projection matrix from perspective
matProj = MatrixPerspective ( camera . fovy * DEG2RAD , ( ( double ) GetScreenWidth ( ) / ( double ) GetScreenHeight ( ) ) , 0.01 , 1000.0 ) ;
}
2018-04-19 21:19:53 +03:00
else if ( camera . type = = CAMERA_ORTHOGRAPHIC )
2018-03-25 01:31:06 +03:00
{
float aspect = ( float ) screenWidth / ( float ) screenHeight ;
double top = camera . fovy / 2.0 ;
double right = top * aspect ;
2018-11-06 17:10:50 +03:00
2018-03-25 01:31:06 +03:00
// Calculate projection matrix from orthographic
matProj = MatrixOrtho ( - right , right , - top , top , 0.01 , 1000.0 ) ;
}
2017-07-22 12:02:40 +03:00
// Unproject far/near points
2017-07-17 01:33:40 +03:00
Vector3 nearPoint = rlUnproject ( ( Vector3 ) { deviceCoords . x , deviceCoords . y , 0.0f } , matProj , matView ) ;
Vector3 farPoint = rlUnproject ( ( Vector3 ) { deviceCoords . x , deviceCoords . y , 1.0f } , matProj , matView ) ;
2017-05-09 23:03:46 +03:00
2018-03-25 01:31:06 +03:00
// Unproject the mouse cursor in the near plane.
2018-11-06 17:10:50 +03:00
// We need this as the source position because orthographic projects, compared to perspect doesn't have a
2018-03-25 01:31:06 +03:00
// convergence point, meaning that the "eye" of the camera is more like a plane than a point.
Vector3 cameraPlanePointerPos = rlUnproject ( ( Vector3 ) { deviceCoords . x , deviceCoords . y , - 1.0f } , matProj , matView ) ;
2017-05-09 23:03:46 +03:00
// Calculate normalized direction vector
2018-04-02 16:16:45 +03:00
Vector3 direction = Vector3Normalize ( Vector3Subtract ( farPoint , nearPoint ) ) ;
2017-05-09 23:03:46 +03:00
2018-04-19 21:19:53 +03:00
if ( camera . type = = CAMERA_PERSPECTIVE ) ray . position = camera . position ;
else if ( camera . type = = CAMERA_ORTHOGRAPHIC ) ray . position = cameraPlanePointerPos ;
2018-03-25 01:31:06 +03:00
2017-05-09 23:03:46 +03:00
// Apply calculated vectors to ray
ray . direction = direction ;
return ray ;
}
// Returns the screen space position from a 3d world space position
Vector2 GetWorldToScreen ( Vector3 position , Camera camera )
{
// Calculate projection matrix (from perspective instead of frustum
2018-05-17 01:58:58 +03:00
Matrix matProj = MatrixIdentity ( ) ;
2018-03-25 01:31:06 +03:00
2018-05-17 01:58:58 +03:00
if ( camera . type = = CAMERA_PERSPECTIVE )
2018-03-25 01:31:06 +03:00
{
// Calculate projection matrix from perspective
matProj = MatrixPerspective ( camera . fovy * DEG2RAD , ( ( double ) GetScreenWidth ( ) / ( double ) GetScreenHeight ( ) ) , 0.01 , 1000.0 ) ;
}
2018-05-17 01:58:58 +03:00
else if ( camera . type = = CAMERA_ORTHOGRAPHIC )
2018-03-25 01:31:06 +03:00
{
float aspect = ( float ) screenWidth / ( float ) screenHeight ;
double top = camera . fovy / 2.0 ;
double right = top * aspect ;
2018-11-06 17:10:50 +03:00
2018-03-25 01:31:06 +03:00
// Calculate projection matrix from orthographic
matProj = MatrixOrtho ( - right , right , - top , top , 0.01 , 1000.0 ) ;
}
2017-05-09 23:03:46 +03:00
// Calculate view matrix from camera look at (and transpose it)
Matrix matView = MatrixLookAt ( camera . position , camera . target , camera . up ) ;
// Convert world position vector to quaternion
Quaternion worldPos = { position . x , position . y , position . z , 1.0f } ;
// Transform world position to view
2018-03-16 15:47:01 +03:00
worldPos = QuaternionTransform ( worldPos , matView ) ;
2017-05-09 23:03:46 +03:00
// Transform result to projection (clip space position)
2018-03-16 15:47:01 +03:00
worldPos = QuaternionTransform ( worldPos , matProj ) ;
2017-05-09 23:03:46 +03:00
// Calculate normalized device coordinates (inverted y)
Vector3 ndcPos = { worldPos . x / worldPos . w , - worldPos . y / worldPos . w , worldPos . z / worldPos . w } ;
// Calculate 2d screen position vector
Vector2 screenPosition = { ( ndcPos . x + 1.0f ) / 2.0f * ( float ) GetScreenWidth ( ) , ( ndcPos . y + 1.0f ) / 2.0f * ( float ) GetScreenHeight ( ) } ;
return screenPosition ;
}
// Get transform matrix for camera
Matrix GetCameraMatrix ( Camera camera )
{
return MatrixLookAt ( camera . position , camera . target , camera . up ) ;
}
// Set target FPS (maximum)
2013-11-19 02:38:44 +04:00
void SetTargetFPS ( int fps )
{
2016-09-24 00:25:13 +03:00
if ( fps < 1 ) targetTime = 0.0 ;
else targetTime = 1.0 / ( double ) fps ;
2014-09-03 18:51:28 +04:00
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Target time per frame: %02.03f milliseconds " , ( float ) targetTime * 1000 ) ;
2013-11-19 02:38:44 +04:00
}
// Returns current FPS
2017-01-28 02:56:45 +03:00
int GetFPS ( void )
2013-11-19 02:38:44 +04:00
{
2017-03-05 21:17:00 +03:00
return ( int ) ( 1.0f / GetFrameTime ( ) ) ;
2013-11-19 02:38:44 +04:00
}
2017-05-09 23:03:46 +03:00
// Returns time in seconds for last frame drawn
2014-09-03 19:06:10 +04:00
float GetFrameTime ( void )
2013-11-19 02:38:44 +04:00
{
2017-01-28 02:56:45 +03:00
// NOTE: We round value to milliseconds
2017-03-05 21:17:00 +03:00
return ( float ) frameTime ;
2013-11-19 02:38:44 +04:00
}
2017-12-19 16:06:54 +03:00
// 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 )
{
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
return glfwGetTime ( ) ; // Elapsed time since glfwInit()
# endif
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
struct timespec ts ;
clock_gettime ( CLOCK_MONOTONIC , & ts ) ;
uint64_t time = ( uint64_t ) ts . tv_sec * 1000000000LLU + ( uint64_t ) ts . tv_nsec ;
return ( double ) ( time - baseTime ) * 1e-9 ; // Elapsed time since InitTimer()
# endif
}
2018-02-12 13:55:22 +03:00
// Returns hexadecimal value for a Color
int ColorToInt ( Color color )
{
return ( ( ( int ) color . r < < 24 ) | ( ( int ) color . g < < 16 ) | ( ( int ) color . b < < 8 ) | ( int ) color . a ) ;
}
2018-04-29 19:39:46 +03:00
// Returns color normalized as float [0..1]
Vector4 ColorNormalize ( Color color )
{
Vector4 result ;
result . x = ( float ) color . r / 255.0f ;
result . y = ( float ) color . g / 255.0f ;
result . z = ( float ) color . b / 255.0f ;
result . w = ( float ) color . a / 255.0f ;
2018-11-06 17:10:50 +03:00
2018-04-29 19:39:46 +03:00
return result ;
}
2018-02-12 13:55:22 +03:00
// Returns HSV values for a Color
// NOTE: Hue is returned as degrees [0..360]
Vector3 ColorToHSV ( Color color )
{
Vector3 rgb = { ( float ) color . r / 255.0f , ( float ) color . g / 255.0f , ( float ) color . b / 255.0f } ;
Vector3 hsv = { 0.0f , 0.0f , 0.0f } ;
float min , max , delta ;
2019-02-22 15:13:11 +03:00
min = rgb . x < rgb . y ? rgb . x : rgb . y ;
min = min < rgb . z ? min : rgb . z ;
2018-02-12 13:55:22 +03:00
2019-02-22 15:13:11 +03:00
max = rgb . x > rgb . y ? rgb . x : rgb . y ;
max = max > rgb . z ? max : rgb . z ;
2018-02-12 13:55:22 +03:00
hsv . z = max ; // Value
delta = max - min ;
2018-11-06 17:10:50 +03:00
2018-02-12 13:55:22 +03:00
if ( delta < 0.00001f )
{
hsv . y = 0.0f ;
hsv . x = 0.0f ; // Undefined, maybe NAN?
return hsv ;
}
2018-11-06 17:10:50 +03:00
if ( max > 0.0f )
2018-02-12 13:55:22 +03:00
{
// NOTE: If max is 0, this divide would cause a crash
hsv . y = ( delta / max ) ; // Saturation
2018-11-06 17:10:50 +03:00
}
else
2018-02-12 13:55:22 +03:00
{
// NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
hsv . y = 0.0f ;
hsv . x = NAN ; // Undefined
return hsv ;
}
2018-11-06 17:10:50 +03:00
2018-02-12 13:55:22 +03:00
// NOTE: Comparing float values could not work properly
if ( rgb . x > = max ) hsv . x = ( rgb . y - rgb . z ) / delta ; // Between yellow & magenta
else
{
if ( rgb . y > = max ) hsv . x = 2.0f + ( rgb . z - rgb . x ) / delta ; // Between cyan & yellow
else hsv . x = 4.0f + ( rgb . x - rgb . y ) / delta ; // Between magenta & cyan
}
2018-11-06 17:10:50 +03:00
2018-02-12 13:55:22 +03:00
hsv . x * = 60.0f ; // Convert to degrees
if ( hsv . x < 0.0f ) hsv . x + = 360.0f ;
return hsv ;
}
2019-01-14 13:49:00 +03:00
// Returns a Color from HSV values
2019-01-14 15:49:17 +03:00
// Implementation reference: https://en.wikipedia.org/wiki/HSL_and_HSV#Alternative_HSV_conversion
2019-01-14 13:49:00 +03:00
// NOTE: Color->HSV->Color conversion will not yield exactly the same color due to rounding errors
2019-01-24 04:07:47 +03:00
Color ColorFromHSV ( Vector3 hsv )
2019-01-14 13:49:00 +03:00
{
2019-01-14 15:49:17 +03:00
Color color = { 0 , 0 , 0 , 255 } ;
float h = hsv . x , s = hsv . y , v = hsv . z ;
2019-01-24 04:07:47 +03:00
2019-01-14 15:49:17 +03:00
// Red channel
float k = fmod ( ( 5.0f + h / 60.0f ) , 6 ) ;
float t = 4.0f - k ;
2019-02-22 15:13:11 +03:00
k = ( t < k ) ? t : k ;
k = ( k < 1 ) ? k : 1 ;
k = ( k > 0 ) ? k : 0 ;
2019-01-14 15:49:17 +03:00
color . r = ( v - v * s * k ) * 255 ;
// Green channel
k = fmod ( ( 3.0f + h / 60.0f ) , 6 ) ;
t = 4.0f - k ;
2019-02-22 15:13:11 +03:00
k = ( t < k ) ? t : k ;
k = ( k < 1 ) ? k : 1 ;
k = ( k > 0 ) ? k : 0 ;
2019-01-14 15:49:17 +03:00
color . g = ( v - v * s * k ) * 255 ;
2019-01-24 04:07:47 +03:00
2019-01-14 15:49:17 +03:00
// Blue channel
k = fmod ( ( 1.0f + h / 60.0f ) , 6 ) ;
t = 4.0f - k ;
2019-02-22 15:13:11 +03:00
k = ( t < k ) ? t : k ;
k = ( k < 1 ) ? k : 1 ;
k = ( k > 0 ) ? k : 0 ;
2019-01-14 15:49:17 +03:00
color . b = ( v - v * s * k ) * 255 ;
2019-01-24 04:07:47 +03:00
2019-01-14 13:49:00 +03:00
return color ;
}
2013-11-19 02:38:44 +04:00
// Returns a Color struct from hexadecimal value
Color GetColor ( int hexValue )
{
2013-11-23 16:30:54 +04:00
Color color ;
2013-11-19 02:38:44 +04:00
2013-11-23 16:30:54 +04:00
color . r = ( unsigned char ) ( hexValue > > 24 ) & 0xFF ;
color . g = ( unsigned char ) ( hexValue > > 16 ) & 0xFF ;
color . b = ( unsigned char ) ( hexValue > > 8 ) & 0xFF ;
color . a = ( unsigned char ) hexValue & 0xFF ;
2014-09-03 18:51:28 +04:00
2013-11-23 16:30:54 +04:00
return color ;
2013-11-19 02:38:44 +04:00
}
2013-12-19 15:08:06 +04:00
// Returns a random value between min and max (both included)
int GetRandomValue ( int min , int max )
{
2014-01-07 19:53:57 +04:00
if ( min > max )
{
int tmp = max ;
max = min ;
min = tmp ;
}
2018-11-30 03:57:53 +03:00
return ( rand ( ) % ( abs ( max - min ) + 1 ) + min ) ;
2013-12-19 15:08:06 +04:00
}
2017-05-09 23:03:46 +03:00
// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
2014-01-23 15:36:18 +04:00
Color Fade ( Color color , float alpha )
{
2014-09-17 00:51:31 +04:00
if ( alpha < 0.0f ) alpha = 0.0f ;
else if ( alpha > 1.0f ) alpha = 1.0f ;
2016-08-16 12:09:55 +03:00
2017-11-03 14:41:03 +03:00
return ( Color ) { color . r , color . g , color . b , ( unsigned char ) ( 255.0f * alpha ) } ;
2014-01-23 15:36:18 +04:00
}
2017-05-09 23:03:46 +03:00
// Setup window configuration flags (view FLAGS)
2017-12-24 18:47:33 +03:00
void SetConfigFlags ( unsigned char flags )
2014-10-17 23:11:58 +04:00
{
configFlags = flags ;
if ( configFlags & FLAG_SHOW_LOGO ) showLogo = true ;
if ( configFlags & FLAG_FULLSCREEN_MODE ) fullscreen = true ;
}
2017-05-09 23:03:46 +03:00
// NOTE TraceLog() function is located in [utils.h]
2017-05-11 17:24:40 +03:00
// Takes a screenshot of current screen (saved a .png)
2019-01-05 20:03:09 +03:00
// NOTE: This function could work in any platform but some platforms: PLATFORM_ANDROID and PLATFORM_WEB
// have their own internal file-systems, to dowload image to user file-system some additional mechanism is required
2017-05-11 17:24:40 +03:00
void TakeScreenshot ( const char * fileName )
2017-04-16 14:47:49 +03:00
{
2017-07-17 01:33:40 +03:00
unsigned char * imgData = rlReadScreenPixels ( renderWidth , renderHeight ) ;
2018-09-17 17:56:02 +03:00
Image image = { imgData , renderWidth , renderHeight , 1 , UNCOMPRESSED_R8G8B8A8 } ;
2019-01-24 04:07:47 +03:00
2019-01-05 20:03:09 +03:00
char path [ 512 ] = { 0 } ;
# if defined(PLATFORM_ANDROID)
strcpy ( path , internalDataPath ) ;
strcat ( path , " / " ) ;
strcat ( path , fileName ) ;
# else
strcpy ( path , fileName ) ;
# endif
2019-01-24 04:07:47 +03:00
2019-01-05 20:03:09 +03:00
ExportImage ( image , path ) ;
2019-04-23 15:55:35 +03:00
RL_FREE ( imgData ) ;
2019-01-24 04:07:47 +03:00
2019-01-05 20:03:09 +03:00
# 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 ) ) ) ;
2017-04-16 14:47:49 +03:00
# endif
2019-01-05 20:03:09 +03:00
TraceLog ( LOG_INFO , " Screenshot taken: %s " , path ) ;
2017-04-16 14:47:49 +03:00
}
2018-10-16 11:53:01 +03:00
// Check if the file exists
bool FileExists ( const char * fileName )
{
bool result = false ;
# if defined(_WIN32)
2019-01-05 20:03:09 +03:00
if ( _access ( fileName , 0 ) ! = - 1 ) result = true ;
2018-10-16 11:53:01 +03:00
# else
2019-01-05 20:03:09 +03:00
if ( access ( fileName , F_OK ) ! = - 1 ) result = true ;
2018-10-16 11:53:01 +03:00
# endif
return result ;
}
2017-03-29 01:35:42 +03:00
// Check file extension
bool IsFileExtension ( const char * fileName , const char * ext )
2014-07-23 02:06:24 +04:00
{
2017-03-29 01:35:42 +03:00
bool result = false ;
const char * fileExt ;
2018-04-03 13:42:28 +03:00
2018-03-16 15:09:49 +03:00
if ( ( fileExt = strrchr ( fileName , ' . ' ) ) ! = NULL )
{
2019-01-05 20:03:09 +03:00
# if defined(_WIN32)
2018-03-16 15:09:49 +03:00
result = true ;
int extLen = strlen ( ext ) ;
2018-11-06 17:10:50 +03:00
2018-03-16 15:09:49 +03:00
if ( strlen ( fileExt ) = = extLen )
{
for ( int i = 0 ; i < extLen ; i + + )
{
if ( tolower ( fileExt [ i ] ) ! = tolower ( ext [ i ] ) )
{
result = false ;
break ;
}
}
}
2018-05-17 01:04:12 +03:00
else result = false ;
2019-01-05 20:03:09 +03:00
# else
2018-03-16 15:09:49 +03:00
if ( strcmp ( fileExt , ext ) = = 0 ) result = true ;
2019-01-05 20:03:09 +03:00
# endif
2018-03-16 15:09:49 +03:00
}
2017-03-29 01:35:42 +03:00
return result ;
2014-07-23 02:06:24 +04:00
}
2018-01-02 04:26:05 +03:00
// Get pointer to extension for a filename string
2017-09-08 10:35:54 +03:00
const char * GetExtension ( const char * fileName )
{
const char * dot = strrchr ( fileName , ' . ' ) ;
2018-11-06 17:10:50 +03:00
2018-03-09 13:43:53 +03:00
if ( ! dot | | dot = = fileName ) return NULL ;
2018-11-06 17:10:50 +03:00
2017-09-08 10:35:54 +03:00
return ( dot + 1 ) ;
}
2018-08-25 18:55:25 +03:00
// String pointer reverse break: returns right-most occurrence of charset in s
2018-08-25 10:20:36 +03:00
static const char * strprbrk ( const char * s , const char * charset )
{
2018-08-25 18:55:25 +03:00
const char * latestMatch = NULL ;
for ( ; s = strpbrk ( s , charset ) , s ! = NULL ; latestMatch = s + + ) { }
return latestMatch ;
2018-08-25 10:20:36 +03:00
}
2018-01-02 04:26:05 +03:00
// Get pointer to filename for a path string
const char * GetFileName ( const char * filePath )
{
2018-08-25 10:20:36 +03:00
const char * fileName = strprbrk ( filePath , " \\ / " ) ;
2018-01-02 04:26:05 +03:00
if ( ! fileName | | fileName = = filePath ) return filePath ;
return fileName + 1 ;
}
2018-10-10 13:01:59 +03:00
// Get filename string without extension (memory should be freed)
const char * GetFileNameWithoutExt ( const char * filePath )
{
2019-03-13 12:07:01 +03:00
# define MAX_FILENAMEWITHOUTEXT_LENGTH 64
2019-04-04 14:50:52 +03:00
2019-03-13 12:07:01 +03:00
static char fileName [ MAX_FILENAMEWITHOUTEXT_LENGTH ] ;
memset ( fileName , 0 , MAX_FILENAMEWITHOUTEXT_LENGTH ) ;
2019-04-04 14:50:52 +03:00
2019-03-13 12:07:01 +03:00
strcpy ( fileName , GetFileName ( filePath ) ) ; // Get filename with extension
2019-04-04 14:50:52 +03:00
2019-03-13 12:07:01 +03:00
int len = strlen ( fileName ) ;
2019-04-04 14:50:52 +03:00
2019-03-13 12:07:01 +03:00
for ( int i = 0 ; ( i < len ) & & ( i < MAX_FILENAMEWITHOUTEXT_LENGTH ) ; i + + )
2018-10-10 13:01:59 +03:00
{
2019-03-13 12:07:01 +03:00
if ( fileName [ i ] = = ' . ' )
2018-10-10 13:01:59 +03:00
{
2019-03-13 12:07:01 +03:00
// NOTE: We break on first '.' found
fileName [ i ] = ' \0 ' ;
break ;
2018-11-06 17:10:50 +03:00
}
2018-10-10 13:01:59 +03:00
}
2019-03-13 12:07:01 +03:00
return fileName ;
2018-10-10 13:01:59 +03:00
}
2018-01-02 04:26:05 +03:00
2017-05-11 17:24:40 +03:00
// Get directory for a given fileName (with path)
const char * GetDirectoryPath ( const char * fileName )
{
2018-08-25 10:20:36 +03:00
const char * lastSlash = NULL ;
2018-10-25 17:09:38 +03:00
static char filePath [ MAX_FILEPATH_LENGTH ] ;
memset ( filePath , 0 , MAX_FILEPATH_LENGTH ) ;
2018-08-25 10:20:36 +03:00
lastSlash = strprbrk ( fileName , " \\ / " ) ;
2018-09-25 13:53:31 +03:00
if ( ! lastSlash ) return NULL ;
2018-08-25 10:27:41 +03:00
2018-10-11 00:55:36 +03:00
// NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
2017-05-11 17:24:40 +03:00
strncpy ( filePath , fileName , strlen ( fileName ) - ( strlen ( lastSlash ) - 1 ) ) ;
2018-10-11 00:55:36 +03:00
filePath [ strlen ( fileName ) - strlen ( lastSlash ) ] = ' \0 ' ; // Add '\0' manually
2018-08-25 10:20:36 +03:00
2017-05-11 17:24:40 +03:00
return filePath ;
}
// Get current working directory
const char * GetWorkingDirectory ( void )
{
2018-10-25 17:09:38 +03:00
static char currentDir [ MAX_FILEPATH_LENGTH ] ;
memset ( currentDir , 0 , MAX_FILEPATH_LENGTH ) ;
2018-11-06 17:10:50 +03:00
2018-10-25 17:09:38 +03:00
GETCWD ( currentDir , MAX_FILEPATH_LENGTH - 1 ) ;
2018-11-06 17:10:50 +03:00
2017-05-11 17:24:40 +03:00
return currentDir ;
}
2018-10-25 17:18:44 +03:00
// Get filenames in a directory path (max 512 files)
2018-10-08 13:29:02 +03:00
// NOTE: Files count is returned by parameters pointer
char * * GetDirectoryFiles ( const char * dirPath , int * fileCount )
{
# define MAX_DIRECTORY_FILES 512
2018-11-06 17:10:50 +03:00
2018-10-08 13:29:02 +03:00
ClearDirectoryFiles ( ) ;
// Memory allocation for MAX_DIRECTORY_FILES
2019-04-23 15:55:35 +03:00
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 ) ;
2018-11-06 17:10:50 +03:00
2018-10-08 13:29:02 +03:00
int counter = 0 ;
struct dirent * ent ;
DIR * dir = opendir ( dirPath ) ;
2018-11-06 17:10:50 +03:00
2018-10-08 13:29:02 +03:00
if ( dir ! = NULL ) // It's a directory
{
2018-11-06 17:10:50 +03:00
// TODO: Reading could be done in two passes,
2018-10-08 13:29:02 +03:00
// first one to count files and second one to read names
// That way we can allocate required memory, instead of a limited pool
2018-11-06 17:10:50 +03:00
2018-10-08 13:29:02 +03:00
while ( ( ent = readdir ( dir ) ) ! = NULL )
{
strcpy ( dirFilesPath [ counter ] , ent - > d_name ) ;
counter + + ;
}
2018-11-06 17:10:50 +03:00
2018-10-08 13:29:02 +03:00
closedir ( dir ) ;
}
else TraceLog ( LOG_WARNING , " Can not open directory... \n " ) ; // Maybe it's a file...
dirFilesCount = counter ;
* fileCount = dirFilesCount ;
2018-11-06 17:10:50 +03:00
2018-10-08 13:29:02 +03:00
return dirFilesPath ;
}
// Clear directory files paths buffers
void ClearDirectoryFiles ( void )
{
if ( dirFilesCount > 0 )
{
2019-04-23 15:55:35 +03:00
for ( int i = 0 ; i < dirFilesCount ; i + + ) RL_FREE ( dirFilesPath [ i ] ) ;
2018-10-08 13:29:02 +03:00
2019-04-23 15:55:35 +03:00
RL_FREE ( dirFilesPath ) ;
2018-10-08 13:29:02 +03:00
dirFilesCount = 0 ;
}
}
2017-05-11 17:24:40 +03:00
// Change working directory, returns true if success
bool ChangeDirectory ( const char * dir )
{
return ( CHDIR ( dir ) = = 0 ) ;
}
2017-05-09 23:03:46 +03:00
// Check if a file has been dropped into window
2015-07-29 22:44:27 +03:00
bool IsFileDropped ( void )
{
if ( dropFilesCount > 0 ) return true ;
else return false ;
}
2017-05-09 23:03:46 +03:00
// Get dropped files names
2015-07-29 22:44:27 +03:00
char * * GetDroppedFiles ( int * count )
{
* count = dropFilesCount ;
return dropFilesPath ;
}
// Clear dropped files paths buffer
void ClearDroppedFiles ( void )
{
if ( dropFilesCount > 0 )
{
2019-04-23 15:55:35 +03:00
for ( int i = 0 ; i < dropFilesCount ; i + + ) RL_FREE ( dropFilesPath [ i ] ) ;
2016-08-16 12:09:55 +03:00
2019-04-23 15:55:35 +03:00
RL_FREE ( dropFilesPath ) ;
2016-08-16 12:09:55 +03:00
2015-07-29 22:44:27 +03:00
dropFilesCount = 0 ;
}
2018-04-29 12:49:10 +03:00
}
2015-07-29 22:44:27 +03:00
2018-10-13 16:59:17 +03:00
// Get file modification time (last write time)
2018-11-26 19:15:00 +03:00
long GetFileModTime ( const char * fileName )
2018-10-12 15:53:36 +03:00
{
2018-10-13 16:59:17 +03:00
struct stat result = { 0 } ;
2018-11-06 17:10:50 +03:00
2018-10-12 15:53:36 +03:00
if ( stat ( fileName , & result ) = = 0 )
{
time_t mod = result . st_mtime ;
2018-11-06 17:10:50 +03:00
2018-10-13 16:59:17 +03:00
return ( long ) mod ;
2018-10-12 15:53:36 +03:00
}
2018-11-06 17:10:50 +03:00
2018-10-12 15:53:36 +03:00
return 0 ;
}
2017-05-09 23:03:46 +03:00
// Save integer value to storage file (to defined position)
2016-01-04 17:12:34 +03:00
// NOTE: Storage positions is directly related to file memory layout (4 bytes each integer)
void StorageSaveValue ( int position , int value )
{
FILE * storageFile = NULL ;
2016-08-16 12:09:55 +03:00
2019-01-05 20:03:09 +03:00
char path [ 512 ] = { 0 } ;
2016-05-31 01:01:19 +03:00
# if defined(PLATFORM_ANDROID)
strcpy ( path , internalDataPath ) ;
strcat ( path , " / " ) ;
strcat ( path , STORAGE_FILENAME ) ;
# else
strcpy ( path , STORAGE_FILENAME ) ;
# endif
2016-01-04 17:12:34 +03:00
// Try open existing file to append data
2016-08-16 12:09:55 +03:00
storageFile = fopen ( path , " rb+ " ) ;
2016-01-04 17:12:34 +03:00
// If file doesn't exist, create a new storage data file
2016-05-31 01:01:19 +03:00
if ( ! storageFile ) storageFile = fopen ( path , " wb " ) ;
2016-01-04 17:12:34 +03:00
2017-07-02 13:35:13 +03:00
if ( ! storageFile ) TraceLog ( LOG_WARNING , " Storage data file could not be created " ) ;
2016-01-04 17:12:34 +03:00
else
{
// Get file size
fseek ( storageFile , 0 , SEEK_END ) ;
int fileSize = ftell ( storageFile ) ; // Size in bytes
fseek ( storageFile , 0 , SEEK_SET ) ;
2016-08-16 12:09:55 +03:00
2018-10-29 18:18:06 +03:00
if ( fileSize < ( position * sizeof ( int ) ) ) TraceLog ( LOG_WARNING , " Storage position could not be found " ) ;
2016-01-04 17:12:34 +03:00
else
{
2018-10-29 18:18:06 +03:00
fseek ( storageFile , ( position * sizeof ( int ) ) , SEEK_SET ) ;
fwrite ( & value , 1 , sizeof ( int ) , storageFile ) ;
2016-01-04 17:12:34 +03:00
}
2016-08-16 12:09:55 +03:00
2016-01-04 17:12:34 +03:00
fclose ( storageFile ) ;
}
}
2017-05-09 23:03:46 +03:00
// Load integer value from storage file (from defined position)
2016-01-04 17:12:34 +03:00
// NOTE: If requested position could not be found, value 0 is returned
int StorageLoadValue ( int position )
{
int value = 0 ;
2016-08-16 12:09:55 +03:00
2019-01-05 20:03:09 +03:00
char path [ 512 ] = { 0 } ;
2016-05-31 01:01:19 +03:00
# if defined(PLATFORM_ANDROID)
strcpy ( path , internalDataPath ) ;
strcat ( path , " / " ) ;
strcat ( path , STORAGE_FILENAME ) ;
# else
strcpy ( path , STORAGE_FILENAME ) ;
# endif
2016-08-16 12:09:55 +03:00
2016-01-04 17:12:34 +03:00
// Try open existing file to append data
2016-08-16 12:09:55 +03:00
FILE * storageFile = fopen ( path , " rb " ) ;
2016-01-04 17:12:34 +03:00
2017-07-02 13:35:13 +03:00
if ( ! storageFile ) TraceLog ( LOG_WARNING , " Storage data file could not be found " ) ;
2016-01-04 17:12:34 +03:00
else
{
// Get file size
fseek ( storageFile , 0 , SEEK_END ) ;
2017-02-12 01:17:56 +03:00
int fileSize = ftell ( storageFile ) ; // Size in bytes
2016-01-04 17:12:34 +03:00
rewind ( storageFile ) ;
2016-08-16 12:09:55 +03:00
2017-07-02 13:35:13 +03:00
if ( fileSize < ( position * 4 ) ) TraceLog ( LOG_WARNING , " Storage position could not be found " ) ;
2016-01-04 17:12:34 +03:00
else
{
fseek ( storageFile , ( position * 4 ) , SEEK_SET ) ;
2017-02-12 01:17:56 +03:00
fread ( & value , 4 , 1 , storageFile ) ; // Read 1 element of 4 bytes size
2016-01-04 17:12:34 +03:00
}
2016-08-16 12:09:55 +03:00
2016-01-04 17:12:34 +03:00
fclose ( storageFile ) ;
}
2016-08-16 12:09:55 +03:00
2016-01-04 17:12:34 +03:00
return value ;
}
2018-11-10 01:09:02 +03:00
// Open URL with default system browser (if available)
2018-11-12 16:59:31 +03:00
// NOTE: This function is onlyl safe to use if you control the URL given.
2018-11-12 14:32:41 +03:00
// A user could craft a malicious string performing another action.
2018-11-12 16:59:31 +03:00
// Only call this function yourself not with user input or make sure to check the string yourself.
// CHECK: https://github.com/raysan5/raylib/issues/686
2018-11-10 01:09:02 +03:00
void OpenURL ( const char * url )
{
2019-01-24 04:07:47 +03:00
// Small security check trying to avoid (partially) malicious code...
2018-11-12 16:59:31 +03:00
// sorry for the inconvenience when you hit this point...
2018-11-13 12:53:49 +03:00
if ( strchr ( url , ' \' ' ) ! = NULL )
2018-11-12 16:59:31 +03:00
{
2018-11-13 12:53:49 +03:00
TraceLog ( LOG_WARNING , " Provided URL does not seem to be valid. " ) ;
2019-01-24 04:07:47 +03:00
}
else
2019-01-05 20:03:09 +03:00
{
2019-04-23 15:55:35 +03:00
char * cmd = ( char * ) RL_CALLOC ( strlen ( url ) + 10 , sizeof ( char ) ) ;
2018-11-10 10:36:15 +03:00
2018-11-10 01:09:02 +03:00
# if defined(_WIN32)
2019-02-21 13:28:10 +03:00
sprintf ( cmd , " explorer %s " , url ) ;
2018-11-10 01:09:02 +03:00
# elif defined(__linux__)
2018-11-12 16:59:31 +03:00
sprintf ( cmd , " xdg-open '%s' " , url ) ; // Alternatives: firefox, x-www-browser
2018-11-10 01:09:02 +03:00
# elif defined(__APPLE__)
2018-11-12 16:59:31 +03:00
sprintf ( cmd , " open '%s' " , url ) ;
2018-11-10 10:36:15 +03:00
# endif
2018-11-12 16:59:31 +03:00
system ( cmd ) ;
2019-04-23 15:55:35 +03:00
RL_FREE ( cmd ) ;
2018-11-12 16:59:31 +03:00
}
2018-11-10 01:09:02 +03:00
}
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions
//----------------------------------------------------------------------------------
2013-11-28 22:59:56 +04:00
// Detect if a key has been pressed once
2013-11-19 02:38:44 +04:00
bool IsKeyPressed ( int key )
2014-09-03 18:51:28 +04:00
{
2014-04-09 22:25:26 +04:00
bool pressed = false ;
2013-11-28 22:59:56 +04:00
2014-12-31 20:03:32 +03:00
if ( ( currentKeyState [ key ] ! = previousKeyState [ key ] ) & & ( currentKeyState [ key ] = = 1 ) ) pressed = true ;
2014-04-09 22:25:26 +04:00
else pressed = false ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
return pressed ;
2013-11-28 22:59:56 +04:00
}
// Detect if a key is being pressed (key held down)
bool IsKeyDown ( int key )
2013-11-19 02:38:44 +04:00
{
2014-09-17 00:51:31 +04:00
if ( GetKeyStatus ( key ) = = 1 ) return true ;
2013-11-23 16:30:54 +04:00
else return false ;
2013-11-19 02:38:44 +04:00
}
2013-11-28 22:59:56 +04:00
// Detect if a key has been released once
2013-11-19 02:38:44 +04:00
bool IsKeyReleased ( int key )
2014-09-03 18:51:28 +04:00
{
2014-04-09 22:25:26 +04:00
bool released = false ;
2017-01-29 01:02:30 +03:00
2014-12-31 20:03:32 +03:00
if ( ( currentKeyState [ key ] ! = previousKeyState [ key ] ) & & ( currentKeyState [ key ] = = 0 ) ) released = true ;
2014-04-09 22:25:26 +04:00
else released = false ;
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
return released ;
2013-11-28 22:59:56 +04:00
}
// Detect if a key is NOT being pressed (key not held down)
bool IsKeyUp ( int key )
2013-11-19 02:38:44 +04:00
{
2014-09-17 00:51:31 +04:00
if ( GetKeyStatus ( key ) = = 0 ) return true ;
2013-11-23 16:30:54 +04:00
else return false ;
2013-11-19 02:38:44 +04:00
}
2015-01-04 20:05:50 +03:00
// Get the last key pressed
int GetKeyPressed ( void )
{
return lastKeyPressed ;
}
2016-04-17 12:19:32 +03:00
// Set a custom key to exit program
// NOTE: default exitKey is ESCAPE
void SetExitKey ( int key )
2014-01-29 00:21:29 +04:00
{
2016-10-17 19:18:13 +03:00
# if !defined(PLATFORM_ANDROID)
2016-04-17 12:19:32 +03:00
exitKey = key ;
2015-02-03 05:47:28 +03:00
# endif
}
2016-02-18 16:05:48 +03:00
// NOTE: Gamepad support not implemented in emscripten GLFW3 (PLATFORM_WEB)
2013-11-19 02:38:44 +04:00
// Detect if a gamepad is available
bool IsGamepadAvailable ( int gamepad )
{
2016-02-18 16:05:48 +03:00
bool result = false ;
2017-01-29 01:02:30 +03:00
2016-10-31 15:56:57 +03:00
# if !defined(PLATFORM_ANDROID)
2016-03-17 14:54:36 +03:00
if ( ( gamepad < MAX_GAMEPADS ) & & gamepadReady [ gamepad ] ) result = true ;
2016-10-31 15:56:57 +03:00
# endif
2014-09-03 18:51:28 +04:00
2016-02-18 16:05:48 +03:00
return result ;
2013-11-19 02:38:44 +04:00
}
2016-11-01 16:39:57 +03:00
// Check gamepad name (if available)
bool IsGamepadName ( int gamepad , const char * name )
{
bool result = false ;
2016-11-17 15:50:56 +03:00
# if !defined(PLATFORM_ANDROID)
2017-01-29 01:02:30 +03:00
const char * gamepadName = NULL ;
2016-11-18 15:39:57 +03:00
2016-11-01 16:39:57 +03:00
if ( gamepadReady [ gamepad ] ) gamepadName = GetGamepadName ( gamepad ) ;
if ( ( name ! = NULL ) & & ( gamepadName ! = NULL ) ) result = ( strcmp ( name , gamepadName ) = = 0 ) ;
2016-11-17 15:50:56 +03:00
# endif
2016-11-01 16:39:57 +03:00
return result ;
}
2016-10-14 01:47:43 +03:00
// Return gamepad internal name id
const char * GetGamepadName ( int gamepad )
{
# if defined(PLATFORM_DESKTOP)
2016-11-01 16:39:57 +03:00
if ( gamepadReady [ gamepad ] ) return glfwGetJoystickName ( gamepad ) ;
2016-10-14 01:47:43 +03:00
else return NULL ;
2016-11-02 15:39:48 +03:00
# elif defined(PLATFORM_RPI)
2016-12-25 04:01:13 +03:00
if ( gamepadReady [ gamepad ] ) ioctl ( gamepadStream [ gamepad ] , JSIOCGNAME ( 64 ) , & gamepadName ) ;
2016-11-02 15:39:48 +03:00
return gamepadName ;
2016-10-14 01:47:43 +03:00
# else
return NULL ;
# endif
}
2016-11-01 16:39:57 +03:00
// Return gamepad axis count
int GetGamepadAxisCount ( int gamepad )
{
2016-11-02 15:39:48 +03:00
# if defined(PLATFORM_RPI)
int axisCount = 0 ;
if ( gamepadReady [ gamepad ] ) ioctl ( gamepadStream [ gamepad ] , JSIOCGAXES , & axisCount ) ;
gamepadAxisCount = axisCount ;
# endif
2016-11-01 16:39:57 +03:00
return gamepadAxisCount ;
}
2013-11-19 02:38:44 +04:00
// Return axis movement vector for a gamepad
2016-03-16 19:52:09 +03:00
float GetGamepadAxisMovement ( int gamepad , int axis )
2013-11-19 02:38:44 +04:00
{
2016-03-16 19:52:09 +03:00
float value = 0 ;
2017-01-29 01:02:30 +03:00
2016-10-31 15:56:57 +03:00
# if !defined(PLATFORM_ANDROID)
if ( ( gamepad < MAX_GAMEPADS ) & & gamepadReady [ gamepad ] & & ( axis < MAX_GAMEPAD_AXIS ) ) value = gamepadAxisState [ gamepad ] [ axis ] ;
# endif
2013-11-23 16:30:54 +04:00
2016-03-16 19:52:09 +03:00
return value ;
2013-11-19 02:38:44 +04:00
}
2014-09-17 00:51:31 +04:00
// Detect if a gamepad button has been pressed once
2013-11-19 02:38:44 +04:00
bool IsGamepadButtonPressed ( int gamepad , int button )
2013-11-28 22:59:56 +04:00
{
2014-04-09 22:25:26 +04:00
bool pressed = false ;
2016-10-31 15:56:57 +03:00
# if !defined(PLATFORM_ANDROID)
2017-01-29 01:02:30 +03:00
if ( ( gamepad < MAX_GAMEPADS ) & & gamepadReady [ gamepad ] & & ( button < MAX_GAMEPAD_BUTTONS ) & &
( currentGamepadState [ gamepad ] [ button ] ! = previousGamepadState [ gamepad ] [ button ] ) & &
2016-10-14 12:14:41 +03:00
( currentGamepadState [ gamepad ] [ button ] = = 1 ) ) pressed = true ;
2016-10-31 15:56:57 +03:00
# endif
2014-09-03 18:51:28 +04:00
2014-04-09 22:25:26 +04:00
return pressed ;
2013-11-28 22:59:56 +04:00
}
2014-09-17 00:51:31 +04:00
// Detect if a gamepad button is being pressed
2013-11-28 22:59:56 +04:00
bool IsGamepadButtonDown ( int gamepad , int button )
2013-11-19 02:38:44 +04:00
{
2016-02-18 16:05:48 +03:00
bool result = false ;
2016-08-16 12:09:55 +03:00
2016-10-31 15:56:57 +03:00
# if !defined(PLATFORM_ANDROID)
2016-10-17 19:18:13 +03:00
if ( ( gamepad < MAX_GAMEPADS ) & & gamepadReady [ gamepad ] & & ( button < MAX_GAMEPAD_BUTTONS ) & &
2016-10-14 12:14:41 +03:00
( currentGamepadState [ gamepad ] [ button ] = = 1 ) ) result = true ;
2016-10-31 15:56:57 +03:00
# endif
2016-08-16 12:09:55 +03:00
2016-02-18 16:05:48 +03:00
return result ;
2013-11-19 02:38:44 +04:00
}
2014-09-17 00:51:31 +04:00
// Detect if a gamepad button has NOT been pressed once
2013-11-19 02:38:44 +04:00
bool IsGamepadButtonReleased ( int gamepad , int button )
2013-11-28 22:59:56 +04:00
{
2014-04-09 22:25:26 +04:00
bool released = false ;
2017-01-29 01:02:30 +03:00
2016-10-31 15:56:57 +03:00
# if !defined(PLATFORM_ANDROID)
2017-01-29 01:02:30 +03:00
if ( ( gamepad < MAX_GAMEPADS ) & & gamepadReady [ gamepad ] & & ( button < MAX_GAMEPAD_BUTTONS ) & &
( currentGamepadState [ gamepad ] [ button ] ! = previousGamepadState [ gamepad ] [ button ] ) & &
2016-10-14 12:14:41 +03:00
( currentGamepadState [ gamepad ] [ button ] = = 0 ) ) released = true ;
2016-10-31 15:56:57 +03:00
# endif
2013-11-28 22:59:56 +04:00
2014-04-09 22:25:26 +04:00
return released ;
2013-11-28 22:59:56 +04:00
}
2014-09-17 00:51:31 +04:00
// Detect if a mouse button is NOT being pressed
2013-11-28 22:59:56 +04:00
bool IsGamepadButtonUp ( int gamepad , int button )
2013-11-19 02:38:44 +04:00
{
2016-02-18 16:05:48 +03:00
bool result = false ;
2014-09-03 18:51:28 +04:00
2016-10-31 15:56:57 +03:00
# if !defined(PLATFORM_ANDROID)
2017-01-29 01:02:30 +03:00
if ( ( gamepad < MAX_GAMEPADS ) & & gamepadReady [ gamepad ] & & ( button < MAX_GAMEPAD_BUTTONS ) & &
2016-10-14 12:14:41 +03:00
( currentGamepadState [ gamepad ] [ button ] = = 0 ) ) result = true ;
2016-10-31 15:56:57 +03:00
# endif
2014-09-17 00:51:31 +04:00
2016-02-18 16:05:48 +03:00
return result ;
}
2016-10-27 14:41:43 +03:00
// Get the last gamepad button pressed
int GetGamepadButtonPressed ( void )
{
return lastGamepadButtonPressed ;
}
2016-04-17 12:19:32 +03:00
// Detect if a mouse button has been pressed once
bool IsMouseButtonPressed ( int button )
{
bool pressed = false ;
2016-08-16 12:09:55 +03:00
2017-09-29 14:56:37 +03:00
// TODO: Review, gestures could be not supported despite being on Android platform!
2016-04-17 12:19:32 +03:00
# if defined(PLATFORM_ANDROID)
2016-04-17 12:36:40 +03:00
if ( IsGestureDetected ( GESTURE_TAP ) ) pressed = true ;
2016-04-17 12:19:32 +03:00
# else
if ( ( currentMouseState [ button ] ! = previousMouseState [ button ] ) & & ( currentMouseState [ button ] = = 1 ) ) pressed = true ;
# endif
2018-11-06 17:10:50 +03:00
2016-04-17 12:19:32 +03:00
return pressed ;
}
// Detect if a mouse button is being pressed
bool IsMouseButtonDown ( int button )
{
bool down = false ;
2016-08-16 12:09:55 +03:00
2016-04-17 12:19:32 +03:00
# if defined(PLATFORM_ANDROID)
2016-04-17 12:36:40 +03:00
if ( IsGestureDetected ( GESTURE_HOLD ) ) down = true ;
2016-04-17 12:19:32 +03:00
# else
if ( GetMouseButtonStatus ( button ) = = 1 ) down = true ;
# endif
2016-08-16 12:09:55 +03:00
2016-04-17 12:19:32 +03:00
return down ;
}
// Detect if a mouse button has been released once
bool IsMouseButtonReleased ( int button )
{
bool released = false ;
2016-08-16 12:09:55 +03:00
2016-04-17 12:19:32 +03:00
# if !defined(PLATFORM_ANDROID)
if ( ( currentMouseState [ button ] ! = previousMouseState [ button ] ) & & ( currentMouseState [ button ] = = 0 ) ) released = true ;
# endif
return released ;
}
// Detect if a mouse button is NOT being pressed
bool IsMouseButtonUp ( int button )
{
bool up = false ;
2016-08-16 12:09:55 +03:00
2016-04-17 12:19:32 +03:00
# if !defined(PLATFORM_ANDROID)
if ( GetMouseButtonStatus ( button ) = = 0 ) up = true ;
# endif
return up ;
}
// Returns mouse position X
int GetMouseX ( void )
{
# if defined(PLATFORM_ANDROID)
return ( int ) touchPosition [ 0 ] . x ;
# else
2019-01-03 15:53:20 +03:00
return ( int ) ( ( mousePosition . x + mouseOffset . x ) * mouseScale . x ) ;
2016-04-17 12:19:32 +03:00
# endif
}
// Returns mouse position Y
int GetMouseY ( void )
{
# if defined(PLATFORM_ANDROID)
return ( int ) touchPosition [ 0 ] . x ;
# else
2019-01-03 15:53:20 +03:00
return ( int ) ( ( mousePosition . y + mouseOffset . y ) * mouseScale . y ) ;
2016-04-17 12:19:32 +03:00
# endif
}
// Returns mouse position XY
Vector2 GetMousePosition ( void )
{
# if defined(PLATFORM_ANDROID)
return GetTouchPosition ( 0 ) ;
# else
2019-01-03 15:53:20 +03:00
return ( Vector2 ) { ( mousePosition . x + mouseOffset . x ) * mouseScale . x , ( mousePosition . y + mouseOffset . y ) * mouseScale . y } ;
2016-04-17 12:19:32 +03:00
# endif
}
// Set mouse position XY
2019-01-03 15:53:20 +03:00
void SetMousePosition ( int x , int y )
2016-04-17 12:19:32 +03:00
{
2019-01-03 15:53:20 +03:00
mousePosition = ( Vector2 ) { ( float ) x , ( float ) y } ;
2016-04-17 12:19:32 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
// NOTE: emscripten not implemented
2019-01-03 15:53:20 +03:00
glfwSetCursorPos ( window , mousePosition . x , mousePosition . y ) ;
2016-04-17 12:19:32 +03:00
# endif
}
2019-01-03 15:53:20 +03:00
// Set mouse offset
2018-03-09 13:43:53 +03:00
// NOTE: Useful when rendering to different size targets
2019-01-03 15:53:20 +03:00
void SetMouseOffset ( int offsetX , int offsetY )
2018-03-09 13:43:53 +03:00
{
2019-01-03 15:53:20 +03:00
mouseOffset = ( Vector2 ) { ( float ) offsetX , ( float ) offsetY } ;
2019-01-02 13:14:55 +03:00
}
2018-03-09 13:43:53 +03:00
// Set mouse scaling
// NOTE: Useful when rendering to different size targets
2019-01-03 15:53:20 +03:00
void SetMouseScale ( float scaleX , float scaleY )
2018-03-09 13:43:53 +03:00
{
2019-02-22 14:12:21 +03:00
mouseScale = ( Vector2 ) { scaleX , scaleY } ;
2018-03-09 13:43:53 +03:00
}
2016-04-17 12:19:32 +03:00
// Returns mouse wheel movement Y
int GetMouseWheelMove ( void )
{
# if defined(PLATFORM_ANDROID)
return 0 ;
# elif defined(PLATFORM_WEB)
return previousMouseWheelY / 100 ;
# else
return previousMouseWheelY ;
# endif
}
2017-05-09 23:03:46 +03:00
// Returns touch position X for touch point 0 (relative to screen size)
2015-10-30 13:30:32 +03:00
int GetTouchX ( void )
{
2016-01-24 21:17:08 +03:00
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
2016-02-10 12:31:06 +03:00
return ( int ) touchPosition [ 0 ] . x ;
2016-01-24 21:17:08 +03:00
# else // PLATFORM_DESKTOP, PLATFORM_RPI
return GetMouseX ( ) ;
# endif
2015-10-30 13:30:32 +03:00
}
2017-05-09 23:03:46 +03:00
// Returns touch position Y for touch point 0 (relative to screen size)
2015-10-30 13:30:32 +03:00
int GetTouchY ( void )
{
2016-01-24 21:17:08 +03:00
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
2016-02-10 12:31:06 +03:00
return ( int ) touchPosition [ 0 ] . y ;
2016-01-24 21:17:08 +03:00
# else // PLATFORM_DESKTOP, PLATFORM_RPI
return GetMouseY ( ) ;
# endif
2015-10-30 13:30:32 +03:00
}
2017-05-09 23:03:46 +03:00
// Returns touch position XY for a touch point index (relative to screen size)
2016-02-12 14:22:56 +03:00
// TODO: Touch position should be scaled depending on display size and render size
2016-02-10 12:31:06 +03:00
Vector2 GetTouchPosition ( int index )
2015-10-30 13:30:32 +03:00
{
2016-02-10 12:31:06 +03:00
Vector2 position = { - 1.0f , - 1.0f } ;
2016-08-16 12:09:55 +03:00
2016-01-24 21:17:08 +03:00
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
2016-02-10 12:31:06 +03:00
if ( index < MAX_TOUCH_POINTS ) position = touchPosition [ index ] ;
2017-07-02 13:35:13 +03:00
else TraceLog ( LOG_WARNING , " Required touch point out of range (Max touch points: %i) " , MAX_TOUCH_POINTS) ;
2015-10-30 13:30:32 +03:00
if ( ( screenWidth > displayWidth ) | | ( screenHeight > displayHeight ) )
{
2016-02-12 14:22:56 +03:00
// TODO: Review touch position scaling for screenSize vs displaySize
2015-10-30 13:30:32 +03:00
position . x = position . x * ( ( float ) screenWidth / ( float ) ( displayWidth - renderOffsetX ) ) - renderOffsetX / 2 ;
position . y = position . y * ( ( float ) screenHeight / ( float ) ( displayHeight - renderOffsetY ) ) - renderOffsetY / 2 ;
}
else
{
position . x = position . x * ( ( float ) renderWidth / ( float ) displayWidth ) - renderOffsetX / 2 ;
position . y = position . y * ( ( float ) renderHeight / ( float ) displayHeight ) - renderOffsetY / 2 ;
}
2018-10-21 02:09:17 +03:00
# elif defined(PLATFORM_RPI)
position = touchPosition [ index ] ;
# else // PLATFORM_DESKTOP
2016-02-10 12:31:06 +03:00
if ( index = = 0 ) position = GetMousePosition ( ) ;
2016-01-24 21:17:08 +03:00
# endif
2015-10-30 13:30:32 +03:00
return position ;
}
2016-01-04 23:00:20 +03:00
2013-11-19 02:38:44 +04:00
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
2014-09-17 00:51:31 +04:00
// 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
2018-02-04 14:26:28 +03:00
// NOTE: returns false in case graphic device could not be created
2018-02-03 14:12:50 +03:00
static bool InitGraphicsDevice ( int width , int height )
2014-09-17 00:51:31 +04:00
{
screenWidth = width ; // User desired width
screenHeight = height ; // User desired height
2019-02-04 19:10:12 +03:00
currentWidth = width ;
currentHeight = height ;
2014-09-17 00:51:31 +04:00
// 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 ( ) ;
2016-06-03 01:53:51 +03:00
2014-12-15 03:08:30 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
2014-09-17 00:51:31 +04:00
glfwSetErrorCallback ( ErrorCallback ) ;
2018-07-29 19:24:46 +03:00
# if defined(__APPLE__)
glfwInitHint ( GLFW_COCOA_CHDIR_RESOURCES , GLFW_FALSE ) ;
# endif
2018-02-03 14:12:50 +03:00
if ( ! glfwInit ( ) )
{
TraceLog ( LOG_WARNING , " Failed to initialize GLFW " ) ;
return false ;
}
2014-09-17 00:51:31 +04:00
2014-12-15 03:08:30 +03:00
// NOTE: Getting video modes is not implemented in emscripten GLFW3 version
# if defined(PLATFORM_DESKTOP)
2014-09-17 00:51:31 +04:00
// Find monitor resolution
2018-02-08 14:00:27 +03:00
GLFWmonitor * monitor = glfwGetPrimaryMonitor ( ) ;
if ( ! monitor )
{
TraceLog ( LOG_WARNING , " Failed to get monitor " ) ;
return false ;
}
const GLFWvidmode * mode = glfwGetVideoMode ( monitor ) ;
2014-09-17 00:51:31 +04:00
displayWidth = mode - > width ;
displayHeight = mode - > height ;
// Screen size security check
if ( screenWidth < = 0 ) screenWidth = displayWidth ;
if ( screenHeight < = 0 ) screenHeight = displayHeight ;
2016-04-17 12:19:32 +03:00
# endif // defined(PLATFORM_DESKTOP)
# if defined(PLATFORM_WEB)
2014-12-15 03:08:30 +03:00
displayWidth = screenWidth ;
displayHeight = screenHeight ;
2016-04-17 12:19:32 +03:00
# endif // defined(PLATFORM_WEB)
2015-02-02 02:57:08 +03:00
2019-04-12 14:31:05 +03:00
glfwDefaultWindowHints ( ) ; // Set default windows hints:
2018-06-02 19:26:57 +03:00
//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
2019-04-12 14:44:16 +03:00
# if defined(PLATFORM_DESKTOP)
2019-04-22 19:46:05 +03:00
// TODO: 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
2019-04-12 14:44:16 +03:00
# endif
2014-09-17 00:51:31 +04:00
2017-03-05 12:55:29 +03:00
// Check some Window creation flags
2019-01-10 19:06:26 +03:00
if ( configFlags & FLAG_WINDOW_HIDDEN ) glfwWindowHint ( GLFW_VISIBLE , GL_FALSE ) ; // Visible window
2019-01-10 16:54:55 +03:00
else glfwWindowHint ( GLFW_VISIBLE , GL_TRUE ) ; // Window initially hidden
2018-06-02 19:26:57 +03:00
if ( configFlags & FLAG_WINDOW_RESIZABLE ) glfwWindowHint ( GLFW_RESIZABLE , GL_TRUE ) ; // Resizable window
2017-03-05 12:55:29 +03:00
else glfwWindowHint ( GLFW_RESIZABLE , GL_FALSE ) ; // Avoid window being resizable
2019-01-10 19:06:26 +03:00
if ( configFlags & FLAG_WINDOW_UNDECORATED ) glfwWindowHint ( GLFW_DECORATED , GLFW_FALSE ) ; // Border and buttons on Window
2018-12-24 19:09:46 +03:00
else glfwWindowHint ( GLFW_DECORATED , GLFW_TRUE ) ; // Decorated window
2018-07-29 08:51:17 +03:00
// FLAG_WINDOW_TRANSPARENT not supported on HTML5 and not included in any released GLFW version yet
# if defined(GLFW_TRANSPARENT_FRAMEBUFFER)
2018-06-02 19:26:57 +03:00
if ( configFlags & FLAG_WINDOW_TRANSPARENT ) glfwWindowHint ( GLFW_TRANSPARENT_FRAMEBUFFER , GLFW_TRUE ) ; // Transparent framebuffer
else glfwWindowHint ( GLFW_TRANSPARENT_FRAMEBUFFER , GLFW_FALSE ) ; // Opaque framebuffer
2018-06-12 14:21:36 +03:00
# endif
2017-01-29 01:02:30 +03:00
2018-06-02 19:26:57 +03:00
if ( configFlags & FLAG_MSAA_4X_HINT ) glfwWindowHint ( GLFW_SAMPLES , 4 ) ; // Tries to enable multisampling x4 (MSAA), default is 0
2018-07-29 08:51:17 +03:00
2015-02-02 02:57:08 +03:00
// NOTE: When asking for an OpenGL context version, most drivers provide highest supported version
2014-09-17 00:51:31 +04:00
// with forward compatibility to older OpenGL versions.
2017-03-05 12:55:29 +03:00
// For example, if using OpenGL 1.1, driver can provide a 4.3 context forward compatible.
2015-02-02 02:57:08 +03:00
2016-06-16 21:25:50 +03:00
// Check selection OpenGL version
if ( rlGetVersion ( ) = = OPENGL_21 )
{
2018-12-24 19:09:46 +03:00
glfwWindowHint ( GLFW_CONTEXT_VERSION_MAJOR , 2 ) ; // Choose OpenGL major version (just hint)
glfwWindowHint ( GLFW_CONTEXT_VERSION_MINOR , 1 ) ; // Choose OpenGL minor version (just hint)
2016-06-16 21:25:50 +03:00
}
else if ( rlGetVersion ( ) = = OPENGL_33 )
{
2018-12-24 19:09:46 +03:00
glfwWindowHint ( GLFW_CONTEXT_VERSION_MAJOR , 3 ) ; // Choose OpenGL major version (just hint)
glfwWindowHint ( GLFW_CONTEXT_VERSION_MINOR , 3 ) ; // Choose OpenGL minor version (just hint)
2015-10-06 18:13:40 +03:00
glfwWindowHint ( GLFW_OPENGL_PROFILE , GLFW_OPENGL_CORE_PROFILE ) ; // Profiles Hint: Only 3.3 and above!
2018-11-06 17:10:50 +03:00
// Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
2017-05-08 03:47:44 +03:00
# if defined(__APPLE__)
2018-12-24 19:09:46 +03:00
glfwWindowHint ( GLFW_OPENGL_FORWARD_COMPAT , GLFW_TRUE ) ; // OSX Requires fordward compatibility
2016-05-14 03:10:05 +03:00
# else
2018-12-24 19:09:46 +03:00
glfwWindowHint ( GLFW_OPENGL_FORWARD_COMPAT , GLFW_FALSE ) ; // Fordward Compatibility Hint: Only 3.3 and above!
2016-05-14 03:10:05 +03:00
# endif
2018-12-24 19:09:46 +03:00
//glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
}
else if ( rlGetVersion ( ) = = 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 ) ; // Alternative: GLFW_EGL_CONTEXT_API (ANGLE)
2014-09-17 00:51:31 +04:00
}
if ( fullscreen )
{
2016-06-26 02:36:06 +03:00
// Obtain recommended displayWidth/displayHeight from a valid videomode for the monitor
2016-08-16 12:09:55 +03:00
int count ;
2016-02-13 19:39:38 +03:00
const GLFWvidmode * modes = glfwGetVideoModes ( glfwGetPrimaryMonitor ( ) , & count ) ;
2016-08-16 12:09:55 +03:00
2016-06-26 02:36:06 +03:00
// Get closest videomode to desired screenWidth/screenHeight
2016-02-13 19:39:38 +03:00
for ( int i = 0 ; i < count ; i + + )
{
2016-06-26 02:36:06 +03:00
if ( modes [ i ] . width > = screenWidth )
{
if ( modes [ i ] . height > = screenHeight )
{
displayWidth = modes [ i ] . width ;
displayHeight = modes [ i ] . height ;
break ;
}
}
2016-02-13 19:39:38 +03:00
}
2016-08-16 12:09:55 +03:00
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_WARNING , " Closest fullscreen videomode: %i x %i " , displayWidth , displayHeight ) ;
2016-06-26 02:36:06 +03:00
// 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...
// At this point we need to manage render size vs screen size
2016-08-16 12:09:55 +03:00
// NOTE: This function uses and modifies global module variables:
2016-06-26 02:36:06 +03:00
// screenWidth/screenHeight - renderWidth/renderHeight - downscaleView
2018-10-08 13:29:02 +03:00
SetupFramebuffer ( displayWidth , displayHeight ) ;
2016-06-26 02:36:06 +03:00
window = glfwCreateWindow ( displayWidth , displayHeight , windowTitle , glfwGetPrimaryMonitor ( ) , NULL ) ;
2016-08-16 12:09:55 +03:00
2016-06-26 02:36:06 +03:00
// NOTE: Full-screen change, not working properly...
//glfwSetWindowMonitor(window, glfwGetPrimaryMonitor(), 0, 0, screenWidth, screenHeight, GLFW_DONT_CARE);
2014-09-17 00:51:31 +04:00
}
else
{
// No-fullscreen window creation
window = glfwCreateWindow ( screenWidth , screenHeight , windowTitle , NULL , NULL ) ;
2016-08-16 12:09:55 +03:00
2018-02-03 16:40:57 +03:00
if ( window )
{
2015-10-21 19:23:49 +03:00
# if defined(PLATFORM_DESKTOP)
2018-02-03 16:40:57 +03:00
// Center window on screen
int windowPosX = displayWidth / 2 - screenWidth / 2 ;
int windowPosY = displayHeight / 2 - screenHeight / 2 ;
2016-08-16 12:09:55 +03:00
2018-02-03 16:40:57 +03:00
if ( windowPosX < 0 ) windowPosX = 0 ;
if ( windowPosY < 0 ) windowPosY = 0 ;
2016-08-16 12:09:55 +03:00
2018-02-03 16:40:57 +03:00
glfwSetWindowPos ( window , windowPosX , windowPosY ) ;
2015-10-21 19:23:49 +03:00
# endif
2018-02-03 16:40:57 +03:00
renderWidth = screenWidth ;
renderHeight = screenHeight ;
}
2014-09-17 00:51:31 +04:00
}
if ( ! window )
{
glfwTerminate ( ) ;
2018-02-03 14:12:50 +03:00
TraceLog ( LOG_WARNING , " GLFW Failed to initialize Window " ) ;
return false ;
2014-09-17 00:51:31 +04:00
}
else
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Display device initialized successfully " ) ;
2014-12-15 03:08:30 +03:00
# if defined(PLATFORM_DESKTOP)
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Display size: %i x %i " , displayWidth , displayHeight ) ;
2014-12-15 03:08:30 +03:00
# endif
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Render size: %i x %i " , renderWidth , renderHeight ) ;
TraceLog ( LOG_INFO , " Screen size: %i x %i " , screenWidth , screenHeight ) ;
TraceLog ( LOG_INFO , " Viewport offsets: %i, %i " , renderOffsetX , renderOffsetY ) ;
2014-09-17 00:51:31 +04:00
}
2016-03-20 16:20:42 +03:00
glfwSetWindowSizeCallback ( window , WindowSizeCallback ) ; // NOTE: Resizing not allowed by default!
2014-09-17 00:51:31 +04:00
glfwSetCursorEnterCallback ( window , CursorEnterCallback ) ;
glfwSetKeyCallback ( window , KeyCallback ) ;
2014-12-31 20:03:32 +03:00
glfwSetMouseButtonCallback ( window , MouseButtonCallback ) ;
2016-03-20 16:20:42 +03:00
glfwSetCursorPosCallback ( window , MouseCursorPosCallback ) ; // Track mouse position changes
2014-12-31 20:03:32 +03:00
glfwSetCharCallback ( window , CharCallback ) ;
2014-09-17 00:51:31 +04:00
glfwSetScrollCallback ( window , ScrollCallback ) ;
2015-01-21 02:13:17 +03:00
glfwSetWindowIconifyCallback ( window , WindowIconifyCallback ) ;
2015-07-29 22:44:27 +03:00
glfwSetDropCallback ( window , WindowDropCallback ) ;
2014-09-17 00:51:31 +04:00
glfwMakeContextCurrent ( window ) ;
2018-11-06 17:10:50 +03:00
2017-03-05 21:17:00 +03:00
// Try to disable GPU V-Sync by default, set framerate using SetTargetFPS()
// NOTE: V-Sync can be enabled by graphic driver configuration
2019-01-04 17:48:25 +03:00
# if !defined(PLATFORM_WEB)
2018-11-06 17:10:50 +03:00
glfwSwapInterval ( 0 ) ;
2019-01-04 17:48:25 +03:00
# endif
2014-09-17 00:51:31 +04:00
2016-02-07 13:35:36 +03:00
# if defined(PLATFORM_DESKTOP)
2016-06-14 16:42:04 +03:00
// Load OpenGL 3.3 extensions
// NOTE: GLFW loader function is passed as parameter
2017-07-19 11:09:34 +03:00
rlLoadExtensions ( glfwGetProcAddress ) ;
2016-02-07 13:35:36 +03:00
# endif
2016-08-16 12:09:55 +03:00
2017-03-05 21:17:00 +03:00
// 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
2015-01-21 02:13:17 +03:00
if ( configFlags & FLAG_VSYNC_HINT )
{
2018-11-06 17:10:50 +03:00
// WARNING: It seems to hits a critical render path in Intel HD Graphics
2015-01-21 02:13:17 +03:00
glfwSwapInterval ( 1 ) ;
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Trying to enable VSYNC " ) ;
2016-08-16 12:09:55 +03:00
}
2016-04-17 12:19:32 +03:00
# endif // defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
2014-09-17 00:51:31 +04:00
2017-11-10 14:37:53 +03:00
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
2014-09-17 00:51:31 +04:00
fullscreen = true ;
// Screen size security check
if ( screenWidth < = 0 ) screenWidth = displayWidth ;
if ( screenHeight < = 0 ) screenHeight = displayHeight ;
# if defined(PLATFORM_RPI)
bcm_host_init ( ) ;
DISPMANX_ELEMENT_HANDLE_T dispmanElement ;
DISPMANX_DISPLAY_HANDLE_T dispmanDisplay ;
DISPMANX_UPDATE_HANDLE_T dispmanUpdate ;
2014-09-30 20:22:21 +04:00
2014-09-17 00:51:31 +04:00
VC_RECT_T dstRect ;
VC_RECT_T srcRect ;
# endif
2015-08-07 18:25:05 +03:00
EGLint samples = 0 ;
EGLint sampleBuffer = 0 ;
2016-08-16 12:09:55 +03:00
if ( configFlags & FLAG_MSAA_4X_HINT )
2015-08-07 18:25:05 +03:00
{
samples = 4 ;
sampleBuffer = 1 ;
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Trying to enable MSAA x4 " ) ;
2015-08-07 18:25:05 +03:00
}
2016-08-16 12:09:55 +03:00
2014-09-17 00:51:31 +04:00
const EGLint framebufferAttribs [ ] =
{
2017-05-08 22:03:48 +03:00
EGL_RENDERABLE_TYPE , EGL_OPENGL_ES2_BIT , // Type of context support -> Required on RPI?
//EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android!
2014-09-19 14:34:25 +04:00
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)
2017-05-09 19:11:02 +03:00
//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)
2015-08-10 11:20:53 +03:00
EGL_DEPTH_SIZE , 16 , // Depth buffer size (Required to use Depth testing!)
2014-09-19 14:34:25 +04:00
//EGL_STENCIL_SIZE, 8, // Stencil buffer size
2015-08-07 18:25:05 +03:00
EGL_SAMPLE_BUFFERS , sampleBuffer , // Activate MSAA
2015-08-10 11:20:53 +03:00
EGL_SAMPLES , samples , // 4x Antialiasing if activated (Free on MALI GPUs)
2014-09-17 00:51:31 +04:00
EGL_NONE
} ;
2017-11-10 14:37:53 +03:00
const EGLint contextAttribs [ ] =
2014-09-17 00:51:31 +04:00
{
EGL_CONTEXT_CLIENT_VERSION , 2 ,
EGL_NONE
} ;
2017-11-10 14:37:53 +03:00
# if defined(PLATFORM_UWP)
const EGLint surfaceAttributes [ ] =
{
// EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER is part of the same optimization as EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER (see above).
// If you have compilation issues with it then please update your Visual Studio templates.
EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER , EGL_TRUE ,
EGL_NONE
} ;
const EGLint defaultDisplayAttributes [ ] =
{
// These are the default display attributes, used to request ANGLE's D3D11 renderer.
// eglInitialize will only succeed with these attributes if the hardware supports D3D11 Feature Level 10_0+.
EGL_PLATFORM_ANGLE_TYPE_ANGLE , EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE ,
// EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that can have large performance benefits on mobile devices.
// Its syntax is subject to change, though. Please update your Visual Studio templates if you experience compilation issues with it.
EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER , EGL_TRUE ,
2018-11-06 17:10:50 +03:00
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that enables ANGLE to automatically call
// the IDXGIDevice3::Trim method on behalf of the application when it gets suspended.
2017-11-10 14:37:53 +03:00
// Calling IDXGIDevice3::Trim when an application is suspended is a Windows Store application certification requirement.
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE , EGL_TRUE ,
EGL_NONE ,
} ;
2018-11-06 17:10:50 +03:00
2017-11-10 14:37:53 +03:00
const EGLint fl9_3DisplayAttributes [ ] =
{
// These can be used to request ANGLE's D3D11 renderer, with D3D11 Feature Level 9_3.
// These attributes are used if the call to eglInitialize fails with the default display attributes.
EGL_PLATFORM_ANGLE_TYPE_ANGLE , EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE ,
EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE , 9 ,
EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE , 3 ,
EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER , EGL_TRUE ,
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE , EGL_TRUE ,
EGL_NONE ,
} ;
const EGLint warpDisplayAttributes [ ] =
{
// These attributes can be used to request D3D11 WARP.
// They are used if eglInitialize fails with both the default display attributes and the 9_3 display attributes.
EGL_PLATFORM_ANGLE_TYPE_ANGLE , EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE ,
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE , EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE ,
EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER , EGL_TRUE ,
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE , EGL_TRUE ,
EGL_NONE ,
} ;
2018-11-06 17:10:50 +03:00
2017-11-10 14:37:53 +03:00
EGLConfig config = NULL ;
// eglGetPlatformDisplayEXT is an alternative to eglGetDisplay. It allows us to pass in display attributes, used to configure D3D11.
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = ( PFNEGLGETPLATFORMDISPLAYEXTPROC ) ( eglGetProcAddress ( " eglGetPlatformDisplayEXT " ) ) ;
2018-02-04 14:26:28 +03:00
if ( ! eglGetPlatformDisplayEXT )
{
TraceLog ( LOG_WARNING , " Failed to get function eglGetPlatformDisplayEXT " ) ;
return false ;
}
2017-11-10 14:37:53 +03:00
//
2018-11-06 17:10:50 +03:00
// To initialize the display, we make three sets of calls to eglGetPlatformDisplayEXT and eglInitialize, with varying
2017-11-10 14:37:53 +03:00
// parameters passed to eglGetPlatformDisplayEXT:
// 1) The first calls uses "defaultDisplayAttributes" as a parameter. This corresponds to D3D11 Feature Level 10_0+.
2018-11-06 17:10:50 +03:00
// 2) If eglInitialize fails for step 1 (e.g. because 10_0+ isn't supported by the default GPU), then we try again
2017-11-10 14:37:53 +03:00
// using "fl9_3DisplayAttributes". This corresponds to D3D11 Feature Level 9_3.
2018-11-06 17:10:50 +03:00
// 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by the default GPU), then we try again
2017-11-10 14:37:53 +03:00
// using "warpDisplayAttributes". This corresponds to D3D11 Feature Level 11_0 on WARP, a D3D11 software rasterizer.
//
2018-11-06 17:10:50 +03:00
2017-11-10 14:37:53 +03:00
// This tries to initialize EGL to D3D11 Feature Level 10_0+. See above comment for details.
display = eglGetPlatformDisplayEXT ( EGL_PLATFORM_ANGLE_ANGLE , EGL_DEFAULT_DISPLAY , defaultDisplayAttributes ) ;
2018-02-04 14:26:28 +03:00
if ( display = = EGL_NO_DISPLAY )
{
TraceLog ( LOG_WARNING , " Failed to initialize EGL display " ) ;
return false ;
}
2018-11-06 17:10:50 +03:00
2017-11-10 14:37:53 +03:00
if ( eglInitialize ( display , NULL , NULL ) = = EGL_FALSE )
{
// This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is unavailable (e.g. on some mobile devices).
display = eglGetPlatformDisplayEXT ( EGL_PLATFORM_ANGLE_ANGLE , EGL_DEFAULT_DISPLAY , fl9_3DisplayAttributes ) ;
2018-02-04 14:26:28 +03:00
if ( display = = EGL_NO_DISPLAY )
{
TraceLog ( LOG_WARNING , " Failed to initialize EGL display " ) ;
return false ;
}
2017-11-10 14:37:53 +03:00
if ( eglInitialize ( display , NULL , NULL ) = = EGL_FALSE )
{
// This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is unavailable on the default GPU.
display = eglGetPlatformDisplayEXT ( EGL_PLATFORM_ANGLE_ANGLE , EGL_DEFAULT_DISPLAY , warpDisplayAttributes ) ;
2018-11-06 17:10:50 +03:00
if ( display = = EGL_NO_DISPLAY )
2018-02-04 14:26:28 +03:00
{
TraceLog ( LOG_WARNING , " Failed to initialize EGL display " ) ;
return false ;
}
2017-11-10 14:37:53 +03:00
if ( eglInitialize ( display , NULL , NULL ) = = EGL_FALSE )
{
// If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
2018-02-04 14:26:28 +03:00
TraceLog ( LOG_WARNING , " Failed to initialize EGL " ) ;
return false ;
2017-11-10 14:37:53 +03:00
}
}
}
2018-10-08 13:29:02 +03:00
//SetupFramebuffer(displayWidth, displayHeight);
2017-11-10 14:37:53 +03:00
EGLint numConfigs = 0 ;
if ( ( eglChooseConfig ( display , framebufferAttribs , & config , 1 , & numConfigs ) = = EGL_FALSE ) | | ( numConfigs = = 0 ) )
{
2018-02-04 14:26:28 +03:00
TraceLog ( LOG_WARNING , " Failed to choose first EGLConfig " ) ;
return false ;
2017-11-10 14:37:53 +03:00
}
// Create a PropertySet and initialize with the EGLNativeWindowType.
//PropertySet^ surfaceCreationProperties = ref new PropertySet();
//surfaceCreationProperties->Insert(ref new String(EGLNativeWindowTypeProperty), window); // CoreWindow^ window
2018-11-06 17:10:50 +03:00
// You can configure the surface to render at a lower resolution and be scaled up to
2017-11-10 14:37:53 +03:00
// the full window size. The scaling is often free on mobile hardware.
//
// One way to configure the SwapChainPanel is to specify precisely which resolution it should render at.
// Size customRenderSurfaceSize = Size(800, 600);
// surfaceCreationProperties->Insert(ref new String(EGLRenderSurfaceSizeProperty), PropertyValue::CreateSize(customRenderSurfaceSize));
//
// Another way is to tell the SwapChainPanel to render at a certain scale factor compared to its size.
// e.g. if the SwapChainPanel is 1920x1280 then setting a factor of 0.5f will make the app render at 960x640
// float customResolutionScale = 0.5f;
// surfaceCreationProperties->Insert(ref new String(EGLRenderResolutionScaleProperty), PropertyValue::CreateSingle(customResolutionScale));
2018-11-06 17:10:50 +03:00
// eglCreateWindowSurface() requires a EGLNativeWindowType parameter,
2017-11-10 14:37:53 +03:00
// In Windows platform: typedef HWND EGLNativeWindowType;
2018-11-06 17:10:50 +03:00
2017-11-10 14:37:53 +03:00
// Property: EGLNativeWindowTypeProperty
// Type: IInspectable
// Description: Set this property to specify the window type to use for creating a surface.
// If this property is missing, surface creation will fail.
//
//const wchar_t EGLNativeWindowTypeProperty[] = L"EGLNativeWindowTypeProperty";
2018-11-06 17:10:50 +03:00
2017-11-10 14:37:53 +03:00
//https://stackoverflow.com/questions/46550182/how-to-create-eglsurface-using-c-winrt-and-angle
2018-11-06 17:10:50 +03:00
2017-11-10 14:37:53 +03:00
//surface = eglCreateWindowSurface(display, config, reinterpret_cast<IInspectable*>(surfaceCreationProperties), surfaceAttributes);
surface = eglCreateWindowSurface ( display , config , uwpWindow , surfaceAttributes ) ;
2018-02-04 14:26:28 +03:00
if ( surface = = EGL_NO_SURFACE )
{
TraceLog ( LOG_WARNING , " Failed to create EGL fullscreen surface " ) ;
return false ;
}
2017-11-10 14:37:53 +03:00
context = eglCreateContext ( display , config , EGL_NO_CONTEXT , contextAttribs ) ;
2018-02-04 14:26:28 +03:00
if ( context = = EGL_NO_CONTEXT )
{
TraceLog ( LOG_WARNING , " Failed to create EGL context " ) ;
return false ;
}
2017-11-10 14:37:53 +03:00
2018-11-06 17:10:50 +03:00
// Get EGL display window size
2018-04-02 15:49:01 +03:00
eglQuerySurface ( display , surface , EGL_WIDTH , & screenWidth ) ;
eglQuerySurface ( display , surface , EGL_HEIGHT , & screenHeight ) ;
2018-11-06 17:10:50 +03:00
2017-11-10 14:37:53 +03:00
# else // PLATFORM_ANDROID, PLATFORM_RPI
2014-09-17 00:51:31 +04:00
EGLint numConfigs ;
// Get an EGL display connection
display = eglGetDisplay ( EGL_DEFAULT_DISPLAY ) ;
2018-11-06 17:10:50 +03:00
if ( display = = EGL_NO_DISPLAY )
2018-02-04 14:26:28 +03:00
{
TraceLog ( LOG_WARNING , " Failed to initialize EGL display " ) ;
return false ;
}
2014-09-17 00:51:31 +04:00
// Initialize the EGL display connection
2018-02-04 14:26:28 +03:00
if ( eglInitialize ( display , NULL , NULL ) = = EGL_FALSE )
{
// If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
TraceLog ( LOG_WARNING , " Failed to initialize EGL " ) ;
return false ;
}
2014-09-17 00:51:31 +04:00
// Get an appropriate EGL framebuffer configuration
eglChooseConfig ( display , framebufferAttribs , & config , 1 , & numConfigs ) ;
// Set rendering API
eglBindAPI ( EGL_OPENGL_ES_API ) ;
// Create an EGL rendering context
context = eglCreateContext ( display , config , EGL_NO_CONTEXT , contextAttribs ) ;
2018-02-04 14:26:28 +03:00
if ( context = = EGL_NO_CONTEXT )
{
TraceLog ( LOG_WARNING , " Failed to create EGL context " ) ;
return false ;
}
2017-11-10 14:37:53 +03:00
# endif
2014-09-17 00:51:31 +04:00
// Create an EGL window surface
//---------------------------------------------------------------------------------
# if defined(PLATFORM_ANDROID)
EGLint displayFormat ;
2018-04-02 15:49:01 +03:00
displayWidth = ANativeWindow_getWidth ( androidApp - > window ) ;
displayHeight = ANativeWindow_getHeight ( androidApp - > window ) ;
2014-09-17 00:51:31 +04:00
// 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 ( 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
2018-10-08 13:29:02 +03:00
SetupFramebuffer ( displayWidth , displayHeight ) ;
2014-09-17 00:51:31 +04:00
2018-04-02 15:49:01 +03:00
ANativeWindow_setBuffersGeometry ( androidApp - > window , renderWidth , renderHeight , displayFormat ) ;
//ANativeWindow_setBuffersGeometry(androidApp->window, 0, 0, displayFormat); // Force use of native display size
2014-09-17 00:51:31 +04:00
2018-04-02 15:49:01 +03:00
surface = eglCreateWindowSurface ( display , config , androidApp - > window , NULL ) ;
2016-04-17 12:19:32 +03:00
# endif // defined(PLATFORM_ANDROID)
2014-09-17 00:51:31 +04:00
2016-04-17 12:19:32 +03:00
# if defined(PLATFORM_RPI)
2014-09-17 00:51:31 +04:00
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
2018-10-08 13:29:02 +03:00
SetupFramebuffer ( displayWidth , displayHeight ) ;
2014-09-17 00:51:31 +04:00
dstRect . x = 0 ;
dstRect . y = 0 ;
dstRect . width = displayWidth ;
dstRect . height = displayHeight ;
srcRect . x = 0 ;
srcRect . y = 0 ;
srcRect . width = renderWidth < < 16 ;
srcRect . height = renderHeight < < 16 ;
// NOTE: RPI dispmanx windowing system takes care of srcRec scaling to dstRec by hardware (no cost)
// Take care that renderWidth/renderHeight fit on displayWidth/displayHeight aspect ratio
2014-09-30 20:22:21 +04:00
VC_DISPMANX_ALPHA_T alpha ;
alpha . flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS ;
2017-05-09 19:11:02 +03:00
alpha . opacity = 255 ; // Set transparency level for framebuffer, requires EGLAttrib: EGL_TRANSPARENT_TYPE
2014-09-30 20:22:21 +04:00
alpha . mask = 0 ;
dispmanDisplay = vc_dispmanx_display_open ( 0 ) ; // LCD
2014-09-17 00:51:31 +04:00
dispmanUpdate = vc_dispmanx_update_start ( 0 ) ;
dispmanElement = vc_dispmanx_element_add ( dispmanUpdate , dispmanDisplay , 0 /*layer*/ , & dstRect , 0 /*src*/ ,
2014-09-30 20:22:21 +04:00
& srcRect , DISPMANX_PROTECTION_NONE , & alpha , 0 /*clamp*/ , DISPMANX_NO_ROTATE ) ;
2014-09-17 00:51:31 +04:00
nativeWindow . element = dispmanElement ;
nativeWindow . width = renderWidth ;
nativeWindow . height = renderHeight ;
vc_dispmanx_update_submit_sync ( dispmanUpdate ) ;
surface = eglCreateWindowSurface ( display , config , & nativeWindow , NULL ) ;
//---------------------------------------------------------------------------------
2016-04-17 12:19:32 +03:00
# endif // defined(PLATFORM_RPI)
2014-09-17 00:51:31 +04:00
// There must be at least one frame displayed before the buffers are swapped
//eglSwapInterval(display, 1);
if ( eglMakeCurrent ( display , surface , surface , context ) = = EGL_FALSE )
{
2018-02-04 14:26:28 +03:00
TraceLog ( LOG_WARNING , " Unable to attach EGL rendering context to EGL surface " ) ;
return false ;
2014-09-17 00:51:31 +04:00
}
else
{
// Grab the width and height of the surface
//eglQuerySurface(display, surface, EGL_WIDTH, &renderWidth);
//eglQuerySurface(display, surface, EGL_HEIGHT, &renderHeight);
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Display device initialized successfully " ) ;
TraceLog ( LOG_INFO , " Display size: %i x %i " , displayWidth , displayHeight ) ;
TraceLog ( LOG_INFO , " Render size: %i x %i " , renderWidth , renderHeight ) ;
TraceLog ( LOG_INFO , " Screen size: %i x %i " , screenWidth , screenHeight ) ;
TraceLog ( LOG_INFO , " Viewport offsets: %i, %i " , renderOffsetX , renderOffsetY ) ;
2014-09-17 00:51:31 +04:00
}
2016-04-17 12:19:32 +03:00
# endif // defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
2014-09-17 00:51:31 +04:00
2018-04-02 15:49:01 +03:00
renderWidth = screenWidth ;
renderHeight = screenHeight ;
2017-11-10 14:37:53 +03:00
2016-06-25 22:28:50 +03:00
// Initialize OpenGL context (states and resources)
2016-07-26 14:02:25 +03:00
// NOTE: screenWidth and screenHeight not used, just stored as globals
2016-06-26 15:13:11 +03:00
rlglInit ( screenWidth , screenHeight ) ;
2016-08-16 12:09:55 +03:00
2016-12-15 10:58:15 +03:00
// Setup default viewport
SetupViewport ( ) ;
2016-06-26 00:28:50 +03:00
// 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)
2016-08-16 12:09:55 +03:00
rlOrtho ( 0 , renderWidth - renderOffsetX , renderHeight - renderOffsetY , 0 , 0.0f , 1.0f ) ;
2016-06-26 00:28:50 +03:00
rlMatrixMode ( RL_MODELVIEW ) ; // Switch back to MODELVIEW matrix
rlLoadIdentity ( ) ; // Reset current matrix (MODELVIEW)
2016-06-03 01:53:51 +03:00
2015-02-26 15:52:03 +03:00
ClearBackground ( RAYWHITE ) ; // Default background color for raylib games :P
# if defined(PLATFORM_ANDROID)
2016-06-24 20:34:47 +03:00
windowReady = true ; // IMPORTANT!
2015-02-26 15:52:03 +03:00
# endif
2018-02-03 14:12:50 +03:00
return true ;
2015-02-26 15:52:03 +03:00
}
2015-03-01 18:00:52 +03:00
2016-12-25 22:42:22 +03:00
// Set viewport parameters
2016-12-15 10:58:15 +03:00
static void SetupViewport ( void )
{
2017-05-08 03:47:44 +03:00
# if defined(__APPLE__)
2016-12-15 10:58:15 +03:00
// 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
rlViewport ( renderOffsetX / 2 , renderOffsetY / 2 , renderWidth - renderOffsetX , renderHeight - renderOffsetY ) ;
# endif
}
2016-05-02 15:11:57 +03:00
// Compute framebuffer size relative to screen size and display size
2016-06-24 20:34:47 +03:00
// NOTE: Global variables renderWidth/renderHeight and renderOffsetX/renderOffsetY can be modified
2018-10-08 13:29:02 +03:00
static void SetupFramebuffer ( int width , int height )
2016-08-16 12:09:55 +03:00
{
2016-05-02 15:11:57 +03:00
// Calculate renderWidth and renderHeight, we have the display size (input params) and the desired screen size (global var)
if ( ( screenWidth > displayWidth ) | | ( screenHeight > displayHeight ) )
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_WARNING , " DOWNSCALING: Required screen size (%ix%i) is bigger than display size (%ix%i) " , screenWidth , screenHeight , displayWidth , displayHeight ) ;
2013-11-19 02:38:44 +04:00
2016-05-02 15:11:57 +03:00
// Downscaling to fit display with border-bars
float widthRatio = ( float ) displayWidth / ( float ) screenWidth ;
float heightRatio = ( float ) displayHeight / ( float ) screenHeight ;
2014-01-29 00:21:29 +04:00
2016-05-02 15:11:57 +03:00
if ( widthRatio < = heightRatio )
{
renderWidth = displayWidth ;
renderHeight = ( int ) round ( ( float ) screenHeight * widthRatio ) ;
renderOffsetX = 0 ;
renderOffsetY = ( displayHeight - renderHeight ) ;
}
else
{
renderWidth = ( int ) round ( ( float ) screenWidth * heightRatio ) ;
renderHeight = displayHeight ;
renderOffsetX = ( displayWidth - renderWidth ) ;
renderOffsetY = 0 ;
}
2014-09-03 18:51:28 +04:00
2016-05-02 15:11:57 +03:00
// NOTE: downscale matrix required!
float scaleRatio = ( float ) renderWidth / ( float ) screenWidth ;
downscaleView = MatrixScale ( scaleRatio , scaleRatio , scaleRatio ) ;
// NOTE: We render to full display resolution!
// We just need to calculate above parameters for downscale matrix and offsets
renderWidth = displayWidth ;
renderHeight = displayHeight ;
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_WARNING , " Downscale matrix generated, content will be rendered at: %i x %i " , renderWidth , renderHeight ) ;
2013-11-23 16:30:54 +04:00
}
2016-05-02 15:11:57 +03:00
else if ( ( screenWidth < displayWidth ) | | ( screenHeight < displayHeight ) )
2013-11-23 16:30:54 +04:00
{
2016-05-02 15:11:57 +03:00
// Required screen size is smaller than display size
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " UPSCALING: Required screen size: %i x %i -> Display size: %i x %i " , screenWidth , screenHeight , displayWidth , displayHeight ) ;
2016-05-02 15:11:57 +03:00
// Upscaling to fit display with border-bars
float displayRatio = ( float ) displayWidth / ( float ) displayHeight ;
float screenRatio = ( float ) screenWidth / ( float ) screenHeight ;
if ( displayRatio < = screenRatio )
{
renderWidth = screenWidth ;
renderHeight = ( int ) round ( ( float ) screenWidth / displayRatio ) ;
renderOffsetX = 0 ;
renderOffsetY = ( renderHeight - screenHeight ) ;
}
else
{
renderWidth = ( int ) round ( ( float ) screenHeight * displayRatio ) ;
renderHeight = screenHeight ;
renderOffsetX = ( renderWidth - screenWidth ) ;
renderOffsetY = 0 ;
}
2013-11-23 16:30:54 +04:00
}
2016-05-02 15:11:57 +03:00
else // screen == display
2016-03-06 21:30:16 +03:00
{
2016-05-02 15:11:57 +03:00
renderWidth = screenWidth ;
renderHeight = screenHeight ;
renderOffsetX = 0 ;
renderOffsetY = 0 ;
2016-03-06 21:30:16 +03:00
}
2014-12-31 20:03:32 +03:00
}
2016-05-02 15:11:57 +03:00
// Initialize hi-resolution timer
static void InitTimer ( void )
2014-12-31 20:03:32 +03:00
{
2018-08-05 01:34:35 +03:00
srand ( ( unsigned int ) time ( NULL ) ) ; // Initialize random seed
2018-11-06 17:10:50 +03:00
2017-05-08 03:37:37 +03:00
# if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32)
timeBeginPeriod ( 1 ) ; // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
# endif
2016-05-02 15:11:57 +03:00
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
struct timespec now ;
if ( clock_gettime ( CLOCK_MONOTONIC , & now ) = = 0 ) // Success
{
baseTime = ( uint64_t ) now . tv_sec * 1000000000LLU + ( uint64_t ) now . tv_nsec ;
}
2017-07-02 13:35:13 +03:00
else TraceLog ( LOG_WARNING , " No hi-resolution timer available " ) ;
2016-02-02 18:43:42 +03:00
# endif
2016-05-02 15:11:57 +03:00
previousTime = GetTime ( ) ; // Get time as double
2016-02-02 18:43:42 +03:00
}
2017-01-28 02:56:45 +03:00
// Wait for some milliseconds (stop program execution)
2017-03-28 20:15:27 +03:00
// 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
2017-03-05 21:17:00 +03:00
static void Wait ( float ms )
{
# if defined(SUPPORT_BUSY_WAIT_LOOP)
2017-01-28 02:56:45 +03:00
double prevTime = GetTime ( ) ;
double nextTime = 0.0 ;
// Busy wait loop
2017-03-05 21:17:00 +03:00
while ( ( nextTime - prevTime ) < ms / 1000.0f ) nextTime = GetTime ( ) ;
# else
2017-05-08 03:37:37 +03:00
# if defined(_WIN32)
2017-03-28 20:15:27 +03:00
Sleep ( ( unsigned int ) ms ) ;
2017-05-08 03:37:37 +03:00
# elif defined(__linux__) || defined(PLATFORM_WEB)
2017-03-05 21:17:00 +03:00
struct timespec req = { 0 } ;
time_t sec = ( int ) ( ms / 1000.0f ) ;
ms - = ( sec * 1000 ) ;
req . tv_sec = sec ;
req . tv_nsec = ms * 1000000L ;
// NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated.
while ( nanosleep ( & req , & req ) = = - 1 ) continue ;
2017-05-08 03:37:37 +03:00
# elif defined(__APPLE__)
2017-03-05 21:17:00 +03:00
usleep ( ms * 1000.0f ) ;
# endif
2017-01-28 02:56:45 +03:00
# endif
}
2016-05-02 15:11:57 +03:00
// Get one key state
static bool GetKeyStatus ( int key )
2014-12-31 20:03:32 +03:00
{
2016-05-02 15:11:57 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
return glfwGetKey ( window , key ) ;
# elif defined(PLATFORM_ANDROID)
2016-10-17 19:18:13 +03:00
// NOTE: Android supports up to 260 keys
if ( key < 0 | | key > 260 ) return false ;
else return currentKeyState [ key ] ;
2016-05-02 15:11:57 +03:00
# elif defined(PLATFORM_RPI)
// NOTE: Keys states are filled in PollInputEvents()
if ( key < 0 | | key > 511 ) return false ;
else return currentKeyState [ key ] ;
# endif
2013-11-19 02:38:44 +04:00
}
2016-05-02 15:11:57 +03:00
// Get one mouse button state
static bool GetMouseButtonStatus ( int button )
2013-12-19 15:08:06 +04:00
{
2016-05-02 15:11:57 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
return glfwGetMouseButton ( window , button ) ;
# elif defined(PLATFORM_ANDROID)
2016-12-25 22:42:22 +03:00
// TODO: Check for virtual mouse?
2016-05-02 15:11:57 +03:00
return false ;
# elif defined(PLATFORM_RPI)
// NOTE: Mouse buttons states are filled in PollInputEvents()
return currentMouseState [ button ] ;
# endif
2013-12-19 15:08:06 +04:00
}
2016-05-02 15:11:57 +03:00
// Poll (store) all input events
static void PollInputEvents ( void )
2013-11-19 02:38:44 +04:00
{
2017-03-21 15:22:59 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
2016-05-02 15:11:57 +03:00
// NOTE: Gestures update must be called every frame to reset gestures correctly
// because ProcessGestureEvent() is just called on an event, not every frame
UpdateGestures ( ) ;
2017-03-21 15:22:59 +03:00
# endif
2017-01-29 01:02:30 +03:00
2016-10-17 19:18:13 +03:00
// Reset last key pressed registered
lastKeyPressed = - 1 ;
2017-01-29 01:02:30 +03:00
2016-11-02 15:39:48 +03:00
# if !defined(PLATFORM_RPI)
// Reset last gamepad button/axis registered state
2016-10-27 14:41:43 +03:00
lastGamepadButtonPressed = - 1 ;
2016-11-01 16:39:57 +03:00
gamepadAxisCount = 0 ;
2016-11-02 15:39:48 +03:00
# endif
2016-08-16 12:09:55 +03:00
2018-10-21 02:09:17 +03:00
# if defined(PLATFORM_RPI)
2019-03-28 21:46:39 +03:00
2018-10-21 02:09:17 +03:00
// Register previous keys states
2019-03-28 21:46:39 +03:00
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)
}
2018-10-21 02:09:17 +03:00
// Register previous mouse states
previousMouseWheelY = currentMouseWheelY ;
currentMouseWheelY = 0 ;
2018-11-06 17:10:50 +03:00
for ( int i = 0 ; i < 3 ; i + + )
2018-10-21 02:09:17 +03:00
{
previousMouseState [ i ] = currentMouseState [ i ] ;
currentMouseState [ i ] = currentMouseStateEvdev [ i ] ;
}
# endif
2016-05-02 15:11:57 +03:00
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
// Mouse input polling
double mouseX ;
double mouseY ;
2014-09-03 18:51:28 +04:00
2016-05-02 15:11:57 +03:00
glfwGetCursorPos ( window , & mouseX , & mouseY ) ;
2015-01-21 02:13:17 +03:00
2016-05-02 15:11:57 +03:00
mousePosition . x = ( float ) mouseX ;
mousePosition . y = ( float ) mouseY ;
2017-01-29 01:02:30 +03:00
2016-05-02 15:11:57 +03:00
// Keyboard input polling (automatically managed by GLFW3 through callback)
// Register previous keys states
for ( int i = 0 ; i < 512 ; i + + ) previousKeyState [ i ] = currentKeyState [ i ] ;
// Register previous mouse states
for ( int i = 0 ; i < 3 ; i + + ) previousMouseState [ i ] = currentMouseState [ i ] ;
previousMouseWheelY = currentMouseWheelY ;
currentMouseWheelY = 0 ;
2016-11-18 15:39:57 +03:00
# endif
# if defined(PLATFORM_DESKTOP)
2016-11-01 16:39:57 +03:00
// Check if gamepads are ready
// NOTE: We do it here in case of disconection
for ( int i = 0 ; i < MAX_GAMEPADS ; i + + )
{
if ( glfwJoystickPresent ( i ) ) gamepadReady [ i ] = true ;
else gamepadReady [ i ] = false ;
}
2017-01-29 01:02:30 +03:00
2016-10-14 12:14:41 +03:00
// Register gamepads buttons events
for ( int i = 0 ; i < MAX_GAMEPADS ; i + + )
2016-10-14 01:47:43 +03:00
{
2016-11-01 16:39:57 +03:00
if ( gamepadReady [ i ] ) // Check if gamepad is available
2016-10-14 01:47:43 +03:00
{
2016-10-14 12:14:41 +03:00
// Register previous gamepad states
for ( int k = 0 ; k < MAX_GAMEPAD_BUTTONS ; k + + ) previousGamepadState [ i ] [ k ] = currentGamepadState [ i ] [ k ] ;
2017-01-29 01:02:30 +03:00
2016-10-14 12:14:41 +03:00
// 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 ) ;
2017-01-29 01:02:30 +03:00
2016-10-14 12:14:41 +03:00
for ( int k = 0 ; ( buttons ! = NULL ) & & ( k < buttonsCount ) & & ( buttonsCount < MAX_GAMEPAD_BUTTONS ) ; k + + )
{
2016-10-27 14:41:43 +03:00
if ( buttons [ k ] = = GLFW_PRESS )
{
currentGamepadState [ i ] [ k ] = 1 ;
lastGamepadButtonPressed = k ;
}
2016-10-14 12:14:41 +03:00
else currentGamepadState [ i ] [ k ] = 0 ;
}
2017-01-29 01:02:30 +03:00
2016-10-14 12:14:41 +03:00
// Get current axis state
const float * axes ;
int axisCount = 0 ;
axes = glfwGetJoystickAxes ( i , & axisCount ) ;
2017-01-29 01:02:30 +03:00
2016-10-14 12:14:41 +03:00
for ( int k = 0 ; ( axes ! = NULL ) & & ( k < axisCount ) & & ( k < MAX_GAMEPAD_AXIS ) ; k + + )
{
gamepadAxisState [ i ] [ k ] = axes [ k ] ;
}
2017-01-29 01:02:30 +03:00
2016-11-01 16:39:57 +03:00
gamepadAxisCount = axisCount ;
2016-10-14 01:47:43 +03:00
}
}
2019-04-04 14:50:52 +03:00
2019-03-05 00:58:20 +03:00
windowResized = false ;
2016-05-02 15:11:57 +03:00
2018-11-01 01:19:29 +03:00
# if defined(SUPPORT_EVENTS_WAITING)
glfwWaitEvents ( ) ;
# else
2016-10-14 01:47:43 +03:00
glfwPollEvents ( ) ; // Register keyboard/mouse events (callbacks)... and window events!
2016-06-03 01:53:51 +03:00
# endif
2018-11-01 01:19:29 +03:00
# endif //defined(PLATFORM_DESKTOP)
2016-05-02 15:11:57 +03:00
2016-12-25 22:42:22 +03:00
// Gamepad support using emscripten API
// NOTE: GLFW3 joystick functionality not available in web
# if defined(PLATFORM_WEB)
// Get number of gamepads connected
int numGamepads = emscripten_get_num_gamepads ( ) ;
2017-01-29 01:02:30 +03:00
2016-12-25 22:42:22 +03:00
for ( int i = 0 ; ( i < numGamepads ) & & ( i < MAX_GAMEPADS ) ; i + + )
{
// Register previous gamepad button states
for ( int k = 0 ; k < MAX_GAMEPAD_BUTTONS ; k + + ) previousGamepadState [ i ] [ k ] = currentGamepadState [ i ] [ k ] ;
2017-01-29 01:02:30 +03:00
2016-12-25 22:42:22 +03:00
EmscriptenGamepadEvent gamepadState ;
2017-01-29 01:02:30 +03:00
2016-12-25 22:42:22 +03:00
int result = emscripten_get_gamepad_status ( i , & gamepadState ) ;
2017-01-29 01:02:30 +03:00
2016-12-25 22:42:22 +03:00
if ( result = = EMSCRIPTEN_RESULT_SUCCESS )
{
// Register buttons data for every connected gamepad
for ( int j = 0 ; ( j < gamepadState . numButtons ) & & ( j < MAX_GAMEPAD_BUTTONS ) ; j + + )
{
if ( gamepadState . digitalButton [ j ] = = 1 )
{
currentGamepadState [ i ] [ j ] = 1 ;
lastGamepadButtonPressed = j ;
}
else currentGamepadState [ i ] [ j ] = 0 ;
2017-01-29 01:02:30 +03:00
2019-04-05 14:12:54 +03:00
//TraceLog(LOG_DEBUG, "Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
2016-12-25 22:42:22 +03:00
}
2017-01-29 01:02:30 +03:00
2016-12-25 22:42:22 +03:00
// 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 ] ;
}
2017-01-29 01:02:30 +03:00
2016-12-25 22:42:22 +03:00
gamepadAxisCount = gamepadState . numAxes ;
}
}
# endif
2016-06-03 01:53:51 +03:00
# if defined(PLATFORM_ANDROID)
2016-05-02 15:11:57 +03:00
// Register previous keys states
2016-10-17 19:18:13 +03:00
// NOTE: Android supports up to 260 keys
for ( int i = 0 ; i < 260 ; i + + ) previousKeyState [ i ] = currentKeyState [ i ] ;
2016-05-02 15:11:57 +03:00
// Poll Events (registered events)
// NOTE: Activity is paused if not enabled (appEnabled)
2019-02-22 15:13:11 +03:00
while ( ( ident = ALooper_pollAll ( appEnabled ? 0 : - 1 , NULL , & events , ( void * * ) & source ) ) > = 0 )
2016-05-02 15:11:57 +03:00
{
// Process this event
2018-04-02 15:49:01 +03:00
if ( source ! = NULL ) source - > process ( androidApp , source ) ;
2016-05-02 15:11:57 +03:00
// NOTE: Never close window, native activity is controlled by the system!
2018-04-02 15:49:01 +03:00
if ( androidApp - > destroyRequested ! = 0 )
2016-05-02 15:11:57 +03:00
{
2017-07-02 13:35:13 +03:00
//TraceLog(LOG_INFO, "Closing Window...");
2016-05-02 15:11:57 +03:00
//windowShouldClose = true;
2018-04-02 15:49:01 +03:00
//ANativeActivity_finish(androidApp->activity);
2016-05-02 15:11:57 +03:00
}
}
2016-06-03 01:53:51 +03:00
# endif
2016-05-02 15:11:57 +03:00
2016-06-03 01:53:51 +03:00
# if defined(PLATFORM_RPI)
2019-03-28 21:46:39 +03:00
// NOTE: Mouse input events polling is done asynchonously in another pthread - EventThread()
2016-05-02 15:11:57 +03:00
// NOTE: Keyboard reading could be done using input_event(s) reading or just read from stdin,
2019-03-28 21:46:39 +03:00
// we now use both methods inside here. 2nd method is still used for legacy purposes (Allows for input trough SSH console)
2016-05-02 15:11:57 +03:00
ProcessKeyboard ( ) ;
// NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread()
# endif
}
// Copy back buffer to front buffers
static void SwapBuffers ( void )
{
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
glfwSwapBuffers ( window ) ;
2016-06-03 01:53:51 +03:00
# endif
2017-11-10 14:37:53 +03:00
# if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_UWP)
2016-05-02 15:11:57 +03:00
eglSwapBuffers ( display , surface ) ;
# endif
}
# if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
// GLFW3 Error Callback, runs on GLFW3 error
static void ErrorCallback ( int error , const char * description )
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_WARNING , " [GLFW3 Error] Code: %i Decription: %s " , error , description ) ;
2016-05-02 15:11:57 +03:00
}
// GLFW3 Srolling Callback, runs on mouse wheel
static void ScrollCallback ( GLFWwindow * window , double xoffset , double yoffset )
{
currentMouseWheelY = ( int ) yoffset ;
}
// GLFW3 Keyboard Callback, runs on key pressed
static void KeyCallback ( GLFWwindow * window , int key , int scancode , int action , int mods )
{
if ( key = = exitKey & & action = = GLFW_PRESS )
{
2018-12-24 19:09:46 +03:00
glfwSetWindowShouldClose ( window , GLFW_TRUE ) ;
2016-05-02 15:11:57 +03:00
// NOTE: Before closing window, while loop must be left!
}
2017-05-11 17:24:40 +03:00
else if ( key = = GLFW_KEY_F12 & & action = = GLFW_PRESS )
{
2019-01-05 20:03:09 +03:00
# if defined(SUPPORT_GIF_RECORDING)
2017-05-18 19:57:11 +03:00
if ( mods = = GLFW_MOD_CONTROL )
{
if ( gifRecording )
{
2017-05-27 15:40:05 +03:00
GifEnd ( ) ;
2017-05-18 19:57:11 +03:00
gifRecording = false ;
2019-01-24 04:07:47 +03:00
2019-01-05 20:03:09 +03:00
# 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
2018-11-06 17:10:50 +03:00
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " End animated GIF recording " ) ;
2017-05-18 19:57:11 +03:00
}
2018-11-06 17:10:50 +03:00
else
2017-05-18 19:57:11 +03:00
{
gifRecording = true ;
gifFramesCounter = 0 ;
2019-01-24 04:07:47 +03:00
2019-01-05 20:03:09 +03:00
char path [ 512 ] = { 0 } ;
# if defined(PLATFORM_ANDROID)
strcpy ( path , internalDataPath ) ;
2019-02-14 02:06:05 +03:00
strcat ( path , TextFormat ( " ./screenrec%03i.gif " , screenshotCounter ) ) ;
2019-01-05 20:03:09 +03:00
# else
2019-02-14 02:06:05 +03:00
strcpy ( path , TextFormat ( " ./screenrec%03i.gif " , screenshotCounter ) ) ;
2019-01-05 20:03:09 +03:00
# endif
2018-11-06 17:10:50 +03:00
2017-05-18 19:57:11 +03:00
// NOTE: delay represents the time between frames in the gif, if we capture a gif frame every
// 10 game frames and each frame trakes 16.6ms (60fps), delay between gif frames should be ~16.6*10.
2019-01-05 20:03:09 +03:00
GifBegin ( path , screenWidth , screenHeight , ( int ) ( GetFrameTime ( ) * 10.0f ) , 8 , false ) ;
2017-05-18 19:57:11 +03:00
screenshotCounter + + ;
2018-11-06 17:10:50 +03:00
2018-12-26 15:26:34 +03:00
TraceLog ( LOG_INFO , " Begin animated GIF recording: %s " , TextFormat ( " screenrec%03i.gif " , screenshotCounter ) ) ;
2017-05-18 19:57:11 +03:00
}
}
else
2019-01-05 20:03:09 +03:00
# endif // SUPPORT_GIF_RECORDING
# if defined(SUPPORT_SCREEN_CAPTURE)
2017-05-18 19:57:11 +03:00
{
2018-12-26 15:26:34 +03:00
TakeScreenshot ( TextFormat ( " screenshot%03i.png " , screenshotCounter ) ) ;
2017-05-18 19:57:11 +03:00
screenshotCounter + + ;
}
2019-01-05 20:03:09 +03:00
# endif // SUPPORT_SCREEN_CAPTURE
2017-05-11 17:24:40 +03:00
}
2016-08-16 12:09:55 +03:00
else
2016-05-02 15:11:57 +03:00
{
currentKeyState [ key ] = action ;
2018-11-06 17:10:50 +03:00
2018-01-01 01:50:22 +03:00
// NOTE: lastKeyPressed already registered on CharCallback()
//if (action == GLFW_PRESS) lastKeyPressed = key;
2016-05-02 15:11:57 +03:00
}
}
// GLFW3 Mouse Button Callback, runs on mouse button pressed
static void MouseButtonCallback ( GLFWwindow * window , int button , int action , int mods )
{
currentMouseState [ button ] = action ;
2016-08-16 12:09:55 +03:00
2017-03-21 15:22:59 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
2016-05-02 15:11:57 +03:00
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Register touch actions
if ( IsMouseButtonPressed ( MOUSE_LEFT_BUTTON ) ) gestureEvent . touchAction = TOUCH_DOWN ;
else if ( IsMouseButtonReleased ( MOUSE_LEFT_BUTTON ) ) gestureEvent . touchAction = TOUCH_UP ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// NOTE: TOUCH_MOVE event is registered in MouseCursorPosCallback()
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Assign a pointer ID
gestureEvent . pointerId [ 0 ] = 0 ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Register touch points count
gestureEvent . pointCount = 1 ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Register touch points position, only one point registered
gestureEvent . position [ 0 ] = GetMousePosition ( ) ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Normalize gestureEvent.position[0] for screenWidth and screenHeight
2016-05-31 20:12:37 +03:00
gestureEvent . position [ 0 ] . x / = ( float ) GetScreenWidth ( ) ;
2016-05-02 15:11:57 +03:00
gestureEvent . position [ 0 ] . y / = ( float ) GetScreenHeight ( ) ;
2016-05-31 20:12:37 +03:00
// Gesture data is sent to gestures system for processing
2016-05-02 15:11:57 +03:00
ProcessGestureEvent ( gestureEvent ) ;
# endif
}
// GLFW3 Cursor Position Callback, runs on mouse move
static void MouseCursorPosCallback ( GLFWwindow * window , double x , double y )
{
2017-03-21 15:22:59 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
2016-05-02 15:11:57 +03:00
// Process mouse events as touches to be able to use mouse-gestures
GestureEvent gestureEvent ;
gestureEvent . touchAction = TOUCH_MOVE ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Assign a pointer ID
gestureEvent . pointerId [ 0 ] = 0 ;
// Register touch points count
gestureEvent . pointCount = 1 ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Register touch points position, only one point registered
gestureEvent . position [ 0 ] = ( Vector2 ) { ( float ) x , ( float ) y } ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
touchPosition [ 0 ] = gestureEvent . position [ 0 ] ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Normalize gestureEvent.position[0] for screenWidth and screenHeight
2016-08-16 12:09:55 +03:00
gestureEvent . position [ 0 ] . x / = ( float ) GetScreenWidth ( ) ;
2016-05-02 15:11:57 +03:00
gestureEvent . position [ 0 ] . y / = ( float ) GetScreenHeight ( ) ;
// Gesture data is sent to gestures system for processing
ProcessGestureEvent ( gestureEvent ) ;
# endif
}
2018-01-01 01:50:22 +03:00
// GLFW3 Char Key Callback, runs on key down (get unicode char value)
2016-05-02 15:11:57 +03:00
static void CharCallback ( GLFWwindow * window , unsigned int key )
2018-11-06 17:10:50 +03:00
{
2018-01-01 01:50:22 +03:00
// NOTE: Registers any key down considering OS keyboard layout but
// do not detects action events, those should be managed by user...
// https://github.com/glfw/glfw/issues/668#issuecomment-166794907
// http://www.glfw.org/docs/latest/input_guide.html#input_char
2018-11-06 17:10:50 +03:00
2016-05-02 15:11:57 +03:00
lastKeyPressed = key ;
}
// GLFW3 CursorEnter Callback, when cursor enters the window
static void CursorEnterCallback ( GLFWwindow * window , int enter )
{
if ( enter = = true ) cursorOnScreen = true ;
else cursorOnScreen = false ;
}
// GLFW3 WindowSize Callback, runs when window is resized
// NOTE: Window resizing not allowed by default
static void WindowSizeCallback ( GLFWwindow * window , int width , int height )
{
2016-06-26 00:28:50 +03:00
// 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)
2016-05-02 15:11:57 +03:00
2018-05-04 17:54:05 +03:00
// Window size must be updated to be used on 3D mode to get new aspect ratio (BeginMode3D())
2017-11-02 22:08:52 +03:00
// 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)
2016-05-02 15:11:57 +03:00
screenWidth = width ;
screenHeight = height ;
renderWidth = width ;
renderHeight = height ;
2019-02-04 19:10:12 +03:00
currentWidth = width ;
currentHeight = height ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// NOTE: Postprocessing texture is not scaled to new size
2019-04-04 14:50:52 +03:00
2019-03-05 00:58:20 +03:00
windowResized = true ;
2016-05-02 15:11:57 +03:00
}
// GLFW3 WindowIconify Callback, runs when window is minimized/restored
2018-10-22 12:48:16 +03:00
static void WindowIconifyCallback ( GLFWwindow * window , int iconified )
2016-05-02 15:11:57 +03:00
{
if ( iconified ) windowMinimized = true ; // The window was iconified
2015-07-29 22:44:27 +03:00
else windowMinimized = false ; // The window was restored
}
2015-02-02 02:57:08 +03:00
2015-07-29 22:44:27 +03:00
// GLFW3 Window Drop Callback, runs when drop files into window
// NOTE: Paths are stored in dinamic memory for further retrieval
// Everytime new files are dropped, old ones are discarded
static void WindowDropCallback ( GLFWwindow * window , int count , const char * * paths )
{
ClearDroppedFiles ( ) ;
2016-08-16 12:09:55 +03:00
2019-04-23 15:55:35 +03:00
dropFilesPath = ( char * * ) RL_MALLOC ( sizeof ( char * ) * count ) ;
2016-08-16 12:09:55 +03:00
2015-07-29 22:44:27 +03:00
for ( int i = 0 ; i < count ; i + + )
2015-01-21 02:13:17 +03:00
{
2019-04-23 15:55:35 +03:00
dropFilesPath [ i ] = ( char * ) RL_MALLOC ( sizeof ( char ) * MAX_FILEPATH_LENGTH ) ;
2015-07-29 22:44:27 +03:00
strcpy ( dropFilesPath [ i ] , paths [ i ] ) ;
2015-01-21 02:13:17 +03:00
}
2015-07-29 22:44:27 +03:00
dropFilesCount = count ;
2015-01-21 02:13:17 +03:00
}
2014-09-17 00:51:31 +04:00
# endif
# if defined(PLATFORM_ANDROID)
// Android: Process activity lifecycle commands
2015-04-22 18:34:42 +03:00
static void AndroidCommandCallback ( struct android_app * app , int32_t cmd )
2014-09-17 00:51:31 +04:00
{
switch ( cmd )
{
case APP_CMD_START :
{
//rendering = true;
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_START " ) ;
2014-09-17 00:51:31 +04:00
} break ;
case APP_CMD_RESUME :
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_RESUME " ) ;
2014-09-17 00:51:31 +04:00
} break ;
case APP_CMD_INIT_WINDOW :
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_INIT_WINDOW " ) ;
2014-09-17 00:51:31 +04:00
if ( app - > window ! = NULL )
{
2016-01-03 15:01:21 +03:00
if ( contextRebindRequired )
{
// Reset screen scaling to full display size
EGLint displayFormat ;
eglGetConfigAttrib ( display , config , EGL_NATIVE_VISUAL_ID , & displayFormat ) ;
ANativeWindow_setBuffersGeometry ( app - > window , renderWidth , renderHeight , displayFormat ) ;
2014-09-17 00:51:31 +04:00
2016-01-03 15:01:21 +03:00
// Recreate display surface and re-attach OpenGL context
surface = eglCreateWindowSurface ( display , config , app - > window , NULL ) ;
eglMakeCurrent ( display , surface , surface , context ) ;
2014-09-17 00:51:31 +04:00
2016-01-03 15:01:21 +03:00
contextRebindRequired = false ;
}
else
{
2016-06-25 22:28:50 +03:00
// Init graphics device (display device and OpenGL context)
InitGraphicsDevice ( screenWidth , screenHeight ) ;
2018-11-06 17:10:50 +03:00
2017-12-19 16:06:54 +03:00
// Init hi-res timer
InitTimer ( ) ;
2016-01-03 15:01:21 +03:00
2019-01-05 20:03:09 +03:00
# if defined(SUPPORT_DEFAULT_FONT)
2016-10-09 14:09:08 +03:00
// Load default font
2016-01-03 15:01:21 +03:00
// NOTE: External function (defined in module: text)
LoadDefaultFont ( ) ;
2019-01-05 20:03:09 +03:00
# endif
2016-05-31 20:12:37 +03:00
2016-01-03 15:01:21 +03:00
// TODO: GPU assets reload in case of lost focus (lost context)
// NOTE: This problem has been solved just unbinding and rebinding context from display
2016-05-31 20:12:37 +03:00
/*
2016-01-03 15:01:21 +03:00
if ( assetsReloadRequired )
{
for ( int i = 0 ; i < assetsCount ; i + + )
{
// TODO: Unload old asset if required
2016-08-16 12:09:55 +03:00
2016-01-03 15:01:21 +03:00
// Load texture again to pointed texture
( * textureAsset + i ) = LoadTexture ( assetPath [ i ] ) ;
}
}
*/
2014-09-17 00:51:31 +04:00
2016-01-03 15:01:21 +03:00
// raylib logo appearing animation (if enabled)
if ( showLogo )
{
SetTargetFPS ( 60 ) ; // Not required on Android
LogoAnimation ( ) ;
}
2014-09-17 00:51:31 +04:00
}
}
} break ;
case APP_CMD_GAINED_FOCUS :
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_GAINED_FOCUS " ) ;
2016-01-03 15:01:21 +03:00
appEnabled = true ;
2015-07-29 22:44:27 +03:00
//ResumeMusicStream();
2014-09-17 00:51:31 +04:00
} break ;
case APP_CMD_PAUSE :
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_PAUSE " ) ;
2014-09-17 00:51:31 +04:00
} break ;
case APP_CMD_LOST_FOCUS :
{
//DrawFrame();
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_LOST_FOCUS " ) ;
2016-01-03 15:01:21 +03:00
appEnabled = false ;
2015-07-29 22:44:27 +03:00
//PauseMusicStream();
2014-09-17 00:51:31 +04:00
} break ;
case APP_CMD_TERM_WINDOW :
{
2016-01-03 15:01:21 +03:00
// Dettach OpenGL context and destroy display surface
// NOTE 1: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
// NOTE 2: In some cases (too many context loaded), OS could unload context automatically... :(
eglMakeCurrent ( display , EGL_NO_SURFACE , EGL_NO_SURFACE , EGL_NO_CONTEXT ) ;
eglDestroySurface ( display , surface ) ;
2016-08-16 12:09:55 +03:00
2016-01-03 15:01:21 +03:00
contextRebindRequired = true ;
2014-09-17 00:51:31 +04:00
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_TERM_WINDOW " ) ;
2014-09-17 00:51:31 +04:00
} break ;
case APP_CMD_SAVE_STATE :
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_SAVE_STATE " ) ;
2014-09-17 00:51:31 +04:00
} break ;
case APP_CMD_STOP :
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_STOP " ) ;
2014-09-17 00:51:31 +04:00
} break ;
case APP_CMD_DESTROY :
{
// TODO: Finish activity?
2018-04-02 15:49:01 +03:00
//ANativeActivity_finish(androidApp->activity);
2014-09-17 00:51:31 +04:00
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_DESTROY " ) ;
2014-09-17 00:51:31 +04:00
} break ;
case APP_CMD_CONFIG_CHANGED :
{
2018-04-02 15:49:01 +03:00
//AConfiguration_fromAssetManager(androidApp->config, androidApp->activity->assetManager);
//print_cur_config(androidApp);
2014-09-17 00:51:31 +04:00
// Check screen orientation here!
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " APP_CMD_CONFIG_CHANGED " ) ;
2014-09-17 00:51:31 +04:00
} break ;
default : break ;
}
}
2016-01-03 15:01:21 +03:00
// Android: Get input events
static int32_t AndroidInputCallback ( struct android_app * app , AInputEvent * event )
{
2018-07-05 20:00:49 +03:00
// If additional inputs are required check:
// https://developer.android.com/ndk/reference/group/input
2016-08-16 12:09:55 +03:00
2016-01-03 15:01:21 +03:00
int type = AInputEvent_getType ( event ) ;
if ( type = = AINPUT_EVENT_TYPE_MOTION )
{
2016-02-10 12:31:06 +03:00
// Get first touch position
touchPosition [ 0 ] . x = AMotionEvent_getX ( event , 0 ) ;
touchPosition [ 0 ] . y = AMotionEvent_getY ( event , 0 ) ;
2016-08-16 12:09:55 +03:00
2016-02-10 12:31:06 +03:00
// Get second touch position
touchPosition [ 1 ] . x = AMotionEvent_getX ( event , 1 ) ;
touchPosition [ 1 ] . y = AMotionEvent_getY ( event , 1 ) ;
2018-11-06 17:10:50 +03:00
2018-07-05 20:00:49 +03:00
// Useful functions for gamepad inputs:
//AMotionEvent_getAction()
//AMotionEvent_getAxisValue()
//AMotionEvent_getButtonState()
2018-11-06 17:10:50 +03:00
2018-07-07 11:15:19 +03:00
// Gamepad dpad button presses capturing
// TODO: That's weird, key input (or button)
2018-11-06 17:10:50 +03:00
// shouldn't come as a TYPE_MOTION event...
2018-07-06 20:33:46 +03:00
int32_t keycode = AKeyEvent_getKeyCode ( event ) ;
2018-07-07 11:15:19 +03:00
if ( AKeyEvent_getAction ( event ) = = AKEY_EVENT_ACTION_DOWN )
2018-07-06 20:33:46 +03:00
{
currentKeyState [ keycode ] = 1 ; // Key down
lastKeyPressed = keycode ;
}
else currentKeyState [ keycode ] = 0 ; // Key up
2018-07-07 11:15:19 +03:00
2016-01-03 15:01:21 +03:00
}
else if ( type = = AINPUT_EVENT_TYPE_KEY )
{
int32_t keycode = AKeyEvent_getKeyCode ( event ) ;
//int32_t AKeyEvent_getMetaState(event);
2016-08-16 12:09:55 +03:00
2016-01-05 14:21:40 +03:00
// Save current button and its state
2016-10-17 19:18:13 +03:00
// NOTE: Android key action is 0 for down and 1 for up
2018-07-07 11:15:19 +03:00
if ( AKeyEvent_getAction ( event ) = = AKEY_EVENT_ACTION_DOWN )
2016-10-17 19:18:13 +03:00
{
2018-07-07 11:15:19 +03:00
currentKeyState [ keycode ] = 1 ; // Key down
2016-10-17 19:18:13 +03:00
lastKeyPressed = keycode ;
}
else currentKeyState [ keycode ] = 0 ; // Key up
2016-01-03 15:01:21 +03:00
2016-01-05 14:21:40 +03:00
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 <activity android:configChanges="orientation|keyboardHidden|screenSize" >
// Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
return 0 ;
2016-08-16 12:09:55 +03:00
}
2016-01-05 14:21:40 +03:00
else if ( ( keycode = = AKEYCODE_BACK ) | | ( keycode = = AKEYCODE_MENU ) )
2016-01-03 15:01:21 +03:00
{
2016-01-04 23:00:20 +03:00
// Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS!
2016-01-03 15:01:21 +03:00
return 1 ;
}
else if ( ( keycode = = AKEYCODE_VOLUME_UP ) | | ( keycode = = AKEYCODE_VOLUME_DOWN ) )
{
// Set default OS behaviour
return 0 ;
}
}
2016-08-16 12:09:55 +03:00
2016-01-03 15:01:21 +03:00
int32_t action = AMotionEvent_getAction ( event ) ;
unsigned int flags = action & AMOTION_EVENT_ACTION_MASK ;
2016-08-16 12:09:55 +03:00
2017-09-29 14:56:37 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
2016-01-03 15:01:21 +03:00
GestureEvent gestureEvent ;
2016-08-16 12:09:55 +03:00
2016-01-03 15:01:21 +03:00
// Register touch actions
if ( flags = = AMOTION_EVENT_ACTION_DOWN ) gestureEvent . touchAction = TOUCH_DOWN ;
else if ( flags = = AMOTION_EVENT_ACTION_UP ) gestureEvent . touchAction = TOUCH_UP ;
else if ( flags = = AMOTION_EVENT_ACTION_MOVE ) gestureEvent . touchAction = TOUCH_MOVE ;
2016-08-16 12:09:55 +03:00
2016-01-03 15:01:21 +03:00
// Register touch points count
2018-07-07 11:15:19 +03:00
// NOTE: Documentation says pointerCount is Always >= 1,
// but in practice it can be 0 or over a million
2016-01-03 15:01:21 +03:00
gestureEvent . pointCount = AMotionEvent_getPointerCount ( event ) ;
2016-08-16 12:09:55 +03:00
2018-07-07 11:15:19 +03:00
// Only enable gestures for 1-3 touch points
if ( ( gestureEvent . pointCount > 0 ) & & ( gestureEvent . pointCount < 4 ) )
{
// Register touch points id
// NOTE: Only two points registered
gestureEvent . pointerId [ 0 ] = AMotionEvent_getPointerId ( event , 0 ) ;
gestureEvent . pointerId [ 1 ] = AMotionEvent_getPointerId ( event , 1 ) ;
2016-08-16 12:09:55 +03:00
2018-07-07 11:15:19 +03:00
// Register touch points position
gestureEvent . position [ 0 ] = ( Vector2 ) { AMotionEvent_getX ( event , 0 ) , AMotionEvent_getY ( event , 0 ) } ;
gestureEvent . position [ 1 ] = ( Vector2 ) { AMotionEvent_getX ( event , 1 ) , AMotionEvent_getY ( event , 1 ) } ;
2016-08-16 12:09:55 +03:00
2018-07-07 11:15:19 +03:00
// Normalize gestureEvent.position[x] for screenWidth and screenHeight
gestureEvent . position [ 0 ] . x / = ( float ) GetScreenWidth ( ) ;
gestureEvent . position [ 0 ] . y / = ( float ) GetScreenHeight ( ) ;
2018-11-06 17:10:50 +03:00
2018-07-07 11:15:19 +03:00
gestureEvent . position [ 1 ] . x / = ( float ) GetScreenWidth ( ) ;
gestureEvent . position [ 1 ] . y / = ( float ) GetScreenHeight ( ) ;
2016-08-16 12:09:55 +03:00
2018-07-07 11:15:19 +03:00
// Gesture data is sent to gestures system for processing
ProcessGestureEvent ( gestureEvent ) ;
}
2017-09-29 14:56:37 +03:00
# else
2017-11-02 22:08:52 +03:00
// Support only simple touch position
if ( flags = = AMOTION_EVENT_ACTION_DOWN )
{
// Get first touch position
touchPosition [ 0 ] . x = AMotionEvent_getX ( event , 0 ) ;
touchPosition [ 0 ] . y = AMotionEvent_getY ( event , 0 ) ;
2018-11-06 17:10:50 +03:00
2017-11-02 22:08:52 +03:00
touchPosition [ 0 ] . x / = ( float ) GetScreenWidth ( ) ;
touchPosition [ 0 ] . y / = ( float ) GetScreenHeight ( ) ;
}
2017-09-29 14:56:37 +03:00
# endif
2016-01-03 15:01:21 +03:00
2017-09-29 14:56:37 +03:00
return 0 ;
2016-01-03 15:01:21 +03:00
}
2014-09-17 00:51:31 +04:00
# endif
2016-05-02 15:11:57 +03:00
# if defined(PLATFORM_WEB)
2016-12-25 22:42:22 +03:00
// Register fullscreen change events
2016-05-02 15:11:57 +03:00
static EM_BOOL EmscriptenFullscreenChangeCallback ( int eventType , const EmscriptenFullscreenChangeEvent * e , void * userData )
2013-11-19 02:38:44 +04:00
{
2016-08-16 12:09:55 +03:00
//isFullscreen: int e->isFullscreen
//fullscreenEnabled: int e->fullscreenEnabled
2016-05-02 15:11:57 +03:00
//fs element nodeName: (char *) e->nodeName
//fs element id: (char *) e->id
//Current element size: (int) e->elementWidth, (int) e->elementHeight
//Screen size:(int) e->screenWidth, (int) e->screenHeight
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
if ( e - > isFullscreen )
2014-09-17 00:51:31 +04:00
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Canvas scaled to fullscreen. ElementSize: (%ix%i), ScreenSize(%ix%i) " , e - > elementWidth , e - > elementHeight , e - > screenWidth , e - > screenHeight ) ;
2016-05-02 15:11:57 +03:00
}
else
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_INFO , " Canvas scaled to windowed. ElementSize: (%ix%i), ScreenSize(%ix%i) " , e - > elementWidth , e - > elementHeight , e - > screenWidth , e - > screenHeight ) ;
2014-09-17 00:51:31 +04:00
}
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// TODO: Depending on scaling factor (screen vs element), calculate factor to scale mouse/touch input
2015-02-02 02:57:08 +03:00
2016-05-02 15:11:57 +03:00
return 0 ;
}
2015-02-02 02:57:08 +03:00
2017-04-08 01:16:03 +03:00
// Register keyboard input events
static EM_BOOL EmscriptenKeyboardCallback ( int eventType , const EmscriptenKeyboardEvent * keyEvent , void * userData )
{
if ( ( eventType = = EMSCRIPTEN_EVENT_KEYPRESS ) & & ( strcmp ( keyEvent - > code , " Escape " ) = = 0 ) )
{
emscripten_exit_pointerlock ( ) ;
}
return 0 ;
}
// Register mouse input events
static EM_BOOL EmscriptenMouseCallback ( int eventType , const EmscriptenMouseEvent * mouseEvent , void * userData )
{
2017-04-28 01:29:50 +03:00
// Lock mouse pointer when click on screen
2017-05-08 22:03:48 +03:00
if ( ( eventType = = EMSCRIPTEN_EVENT_CLICK ) & & toggleCursorLock )
2017-04-08 01:16:03 +03:00
{
EmscriptenPointerlockChangeEvent plce ;
emscripten_get_pointerlock_status ( & plce ) ;
if ( ! plce . isActive ) emscripten_request_pointerlock ( 0 , 1 ) ;
else
{
emscripten_exit_pointerlock ( ) ;
emscripten_get_pointerlock_status ( & plce ) ;
2017-07-02 13:35:13 +03:00
//if (plce.isActive) TraceLog(LOG_WARNING, "Pointer lock exit did not work!");
2017-04-08 01:16:03 +03:00
}
2018-11-06 17:10:50 +03:00
2017-05-08 22:03:48 +03:00
toggleCursorLock = false ;
2017-04-08 01:16:03 +03:00
}
2018-11-06 17:10:50 +03:00
2017-04-08 01:16:03 +03:00
return 0 ;
}
2016-12-25 22:42:22 +03:00
// Register touch input events
2017-04-08 01:16:03 +03:00
static EM_BOOL EmscriptenTouchCallback ( int eventType , const EmscriptenTouchEvent * touchEvent , void * userData )
2016-05-02 15:11:57 +03:00
{
/*
for ( int i = 0 ; i < touchEvent - > numTouches ; i + + )
{
long x , y , id ;
2015-04-06 15:02:29 +03:00
2016-05-02 15:11:57 +03:00
if ( ! touchEvent - > touches [ i ] . isChanged ) continue ;
2014-09-17 00:51:31 +04:00
2016-05-02 15:11:57 +03:00
id = touchEvent - > touches [ i ] . identifier ;
x = touchEvent - > touches [ i ] . canvasX ;
y = touchEvent - > touches [ i ] . canvasY ;
}
2016-08-16 12:09:55 +03:00
2019-04-05 14:12:54 +03:00
TraceLog ( LOG_DEBUG , " %s, numTouches: %d %s%s%s%s " , emscripten_event_type_to_string ( eventType ) , event - > numTouches ,
2019-02-22 15:13:11 +03:00
event - > ctrlKey ? " CTRL " : " " , event - > shiftKey ? " SHIFT " : " " , event - > altKey ? " ALT " : " " , event - > metaKey ? " META " : " " ) ;
2016-01-04 23:00:20 +03:00
2016-08-31 11:27:29 +03:00
for ( int i = 0 ; i < event - > numTouches ; + + i )
2014-09-17 00:51:31 +04:00
{
2016-05-02 15:11:57 +03:00
const EmscriptenTouchPoint * t = & event - > touches [ i ] ;
2016-08-16 12:09:55 +03:00
2019-04-05 14:12:54 +03:00
TraceLog ( LOG_DEBUG , " %ld: screen: (%ld,%ld), client: (%ld,%ld), page: (%ld,%ld), isChanged: %d, onTarget: %d, canvas: (%ld, %ld) " ,
2016-05-02 15:11:57 +03:00
t - > identifier , t - > screenX , t - > screenY , t - > clientX , t - > clientY , t - > pageX , t - > pageY , t - > isChanged , t - > onTarget , t - > canvasX , t - > canvasY ) ;
2014-09-17 00:51:31 +04:00
}
2016-05-02 15:11:57 +03:00
*/
2016-08-16 12:09:55 +03:00
2019-03-25 14:30:20 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
2016-05-02 15:11:57 +03:00
GestureEvent gestureEvent ;
2014-09-17 00:51:31 +04:00
2016-05-02 15:11:57 +03:00
// Register touch actions
if ( eventType = = EMSCRIPTEN_EVENT_TOUCHSTART ) gestureEvent . touchAction = TOUCH_DOWN ;
else if ( eventType = = EMSCRIPTEN_EVENT_TOUCHEND ) gestureEvent . touchAction = TOUCH_UP ;
else if ( eventType = = EMSCRIPTEN_EVENT_TOUCHMOVE ) gestureEvent . touchAction = TOUCH_MOVE ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Register touch points count
gestureEvent . pointCount = touchEvent - > numTouches ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Register touch points id
gestureEvent . pointerId [ 0 ] = touchEvent - > touches [ 0 ] . identifier ;
gestureEvent . pointerId [ 1 ] = touchEvent - > touches [ 1 ] . identifier ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Register touch points position
// NOTE: Only two points registered
// TODO: Touch data should be scaled accordingly!
//gestureEvent.position[0] = (Vector2){ touchEvent->touches[0].canvasX, touchEvent->touches[0].canvasY };
//gestureEvent.position[1] = (Vector2){ touchEvent->touches[1].canvasX, touchEvent->touches[1].canvasY };
gestureEvent . position [ 0 ] = ( Vector2 ) { touchEvent - > touches [ 0 ] . targetX , touchEvent - > touches [ 0 ] . targetY } ;
gestureEvent . position [ 1 ] = ( Vector2 ) { touchEvent - > touches [ 1 ] . targetX , touchEvent - > touches [ 1 ] . targetY } ;
2014-09-17 00:51:31 +04:00
2016-05-02 15:11:57 +03:00
touchPosition [ 0 ] = gestureEvent . position [ 0 ] ;
touchPosition [ 1 ] = gestureEvent . position [ 1 ] ;
2016-08-16 12:09:55 +03:00
2016-05-02 15:11:57 +03:00
// Normalize gestureEvent.position[x] for screenWidth and screenHeight
2016-08-16 12:09:55 +03:00
gestureEvent . position [ 0 ] . x / = ( float ) GetScreenWidth ( ) ;
2016-05-02 15:11:57 +03:00
gestureEvent . position [ 0 ] . y / = ( float ) GetScreenHeight ( ) ;
2016-08-16 12:09:55 +03:00
gestureEvent . position [ 1 ] . x / = ( float ) GetScreenWidth ( ) ;
2016-05-02 15:11:57 +03:00
gestureEvent . position [ 1 ] . y / = ( float ) GetScreenHeight ( ) ;
2016-02-18 16:05:48 +03:00
2016-05-02 15:11:57 +03:00
// Gesture data is sent to gestures system for processing
2016-06-03 19:51:19 +03:00
ProcessGestureEvent ( gestureEvent ) ;
2019-03-25 14:30:20 +03:00
# 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
2016-02-18 16:05:48 +03:00
2016-05-02 15:11:57 +03:00
return 1 ;
2016-02-18 16:05:48 +03:00
}
2016-12-25 22:42:22 +03:00
// Register connected/disconnected gamepads events
static EM_BOOL EmscriptenGamepadCallback ( int eventType , const EmscriptenGamepadEvent * gamepadEvent , void * userData )
{
/*
2019-04-05 14:12:54 +03:00
TraceLog ( LOG_DEBUG , " %s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \" %s \" , mapping: \" %s \" " ,
2019-02-22 15:13:11 +03:00
eventType ! = 0 ? emscripten_event_type_to_string ( eventType ) : " Gamepad state " ,
2016-12-25 22:42:22 +03:00
gamepadEvent - > timestamp , gamepadEvent - > connected , gamepadEvent - > index , gamepadEvent - > numAxes , gamepadEvent - > numButtons , gamepadEvent - > id , gamepadEvent - > mapping ) ;
2017-01-29 01:02:30 +03:00
2019-04-05 14:12:54 +03:00
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 ] ) ;
2016-12-25 22:42:22 +03:00
*/
2017-01-29 01:02:30 +03:00
2016-12-25 22:42:22 +03:00
if ( ( gamepadEvent - > connected ) & & ( gamepadEvent - > index < MAX_GAMEPADS ) ) gamepadReady [ gamepadEvent - > index ] = true ;
else gamepadReady [ gamepadEvent - > index ] = false ;
2017-01-29 01:02:30 +03:00
2016-12-25 22:42:22 +03:00
// TODO: Test gamepadEvent->index
return 0 ;
}
2016-05-02 15:11:57 +03:00
# endif
2016-02-18 16:05:48 +03:00
# if defined(PLATFORM_RPI)
// Initialize Keyboard system (using standard input)
static void InitKeyboard ( void )
{
// NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor
2014-09-17 00:51:31 +04:00
2016-02-18 16:05:48 +03:00
// Make stdin non-blocking (not enough, need to configure to non-canonical mode)
int flags = fcntl ( STDIN_FILENO , F_GETFL , 0 ) ; // F_GETFL: Get the file access mode and the file status flags
fcntl ( STDIN_FILENO , F_SETFL , flags | O_NONBLOCK ) ; // F_SETFL: Set the file status flags to the value specified
// Save terminal keyboard settings and reconfigure terminal with new settings
struct termios keyboardNewSettings ;
tcgetattr ( STDIN_FILENO , & defaultKeyboardSettings ) ; // Get current keyboard settings
keyboardNewSettings = defaultKeyboardSettings ;
// 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 ) ;
2018-12-19 17:31:20 +03:00
// NOTE: Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
2019-01-24 04:07:47 +03:00
2016-02-18 16:05:48 +03:00
// Save old keyboard mode to restore it at the end
if ( ioctl ( STDIN_FILENO , KDGKBMODE , & defaultKeyboardMode ) < 0 )
{
// NOTE: It could mean we are using a remote keyboard through ssh!
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_WARNING , " Could not change keyboard mode (SSH keyboard?) " ) ;
2016-02-18 16:05:48 +03:00
}
else
{
// We reconfigure keyboard mode to get:
2016-08-16 12:09:55 +03:00
// - scancodes (K_RAW)
2016-02-18 16:05:48 +03:00
// - keycodes (K_MEDIUMRAW)
// - ASCII chars (K_XLATE)
// - UNICODE chars (K_UNICODE)
ioctl ( STDIN_FILENO , KDSKBMODE , K_XLATE ) ;
}
// Register keyboard restore when program finishes
atexit ( RestoreKeyboard ) ;
}
2016-02-19 21:57:25 +03:00
// Process keyboard inputs
// TODO: Most probably input reading and processing should be in a separate thread
2016-02-18 16:05:48 +03:00
static void ProcessKeyboard ( void )
{
2016-02-19 21:57:25 +03:00
# define MAX_KEYBUFFER_SIZE 32 // Max size in bytes to read
2016-08-16 12:09:55 +03:00
2014-09-17 00:51:31 +04:00
// Keyboard input polling (fill keys[256] array with status)
2016-02-19 21:57:25 +03:00
int bufferByteCount = 0 ; // Bytes available on the buffer
char keysBuffer [ MAX_KEYBUFFER_SIZE ] ; // Max keys to be read at a time
2014-09-17 00:51:31 +04:00
// Read availables keycodes from stdin
2016-02-19 21:57:25 +03:00
bufferByteCount = read ( STDIN_FILENO , keysBuffer , MAX_KEYBUFFER_SIZE ) ; // POSIX system call
2019-01-24 04:07:47 +03:00
2018-12-19 17:31:20 +03:00
// Reset pressed keys array (it will be filled below)
for ( int i = 0 ; i < 512 ; i + + ) currentKeyState [ i ] = 0 ;
2019-01-24 04:07:47 +03:00
2019-03-28 21:46:39 +03:00
// Check keys from event input workers (This is the new keyboard reading method)
2019-04-05 14:12:54 +03:00
for ( int i = 0 ; i < 512 ; i + + ) currentKeyState [ i ] = currentKeyStateEvdev [ i ] ;
2019-03-28 21:46:39 +03:00
2016-02-19 21:57:25 +03:00
// Fill all read bytes (looking for keys)
for ( int i = 0 ; i < bufferByteCount ; i + + )
2014-09-17 00:51:31 +04:00
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_DEBUG , " Bytes on keysBuffer: %i " , bufferByteCount ) ;
2016-08-16 12:09:55 +03:00
2016-02-19 21:57:25 +03:00
// 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 )
2014-09-17 00:51:31 +04:00
{
2016-02-19 21:57:25 +03:00
// Detect ESC to stop program
if ( bufferByteCount = = 1 ) currentKeyState [ 256 ] = 1 ; // raylib key: KEY_ESCAPE
else
2014-09-17 00:51:31 +04:00
{
2016-02-19 21:57:25 +03:00
if ( keysBuffer [ i + 1 ] = = 0x5b ) // Special function key
2014-09-17 00:51:31 +04:00
{
2016-02-19 21:57:25 +03:00
if ( ( keysBuffer [ i + 2 ] = = 0x5b ) | | ( keysBuffer [ i + 2 ] = = 0x31 ) | | ( keysBuffer [ i + 2 ] = = 0x32 ) )
2014-09-17 00:51:31 +04:00
{
2016-02-19 21:57:25 +03:00
// Process special function keys (F1 - F12)
switch ( keysBuffer [ i + 3 ] )
{
case 0x41 : currentKeyState [ 290 ] = 1 ; break ; // raylib KEY_F1
case 0x42 : currentKeyState [ 291 ] = 1 ; break ; // raylib KEY_F2
case 0x43 : currentKeyState [ 292 ] = 1 ; break ; // raylib KEY_F3
case 0x44 : currentKeyState [ 293 ] = 1 ; break ; // raylib KEY_F4
case 0x45 : currentKeyState [ 294 ] = 1 ; break ; // raylib KEY_F5
case 0x37 : currentKeyState [ 295 ] = 1 ; break ; // raylib KEY_F6
case 0x38 : currentKeyState [ 296 ] = 1 ; break ; // raylib KEY_F7
case 0x39 : currentKeyState [ 297 ] = 1 ; break ; // raylib KEY_F8
case 0x30 : currentKeyState [ 298 ] = 1 ; break ; // raylib KEY_F9
case 0x31 : currentKeyState [ 299 ] = 1 ; break ; // raylib KEY_F10
case 0x33 : currentKeyState [ 300 ] = 1 ; break ; // raylib KEY_F11
case 0x34 : currentKeyState [ 301 ] = 1 ; break ; // raylib KEY_F12
default : break ;
}
2016-08-16 12:09:55 +03:00
2016-02-19 21:57:25 +03:00
if ( keysBuffer [ i + 2 ] = = 0x5b ) i + = 4 ;
else if ( ( keysBuffer [ i + 2 ] = = 0x31 ) | | ( keysBuffer [ i + 2 ] = = 0x32 ) ) i + = 5 ;
2014-09-17 00:51:31 +04:00
}
2016-02-19 21:57:25 +03:00
else
{
switch ( keysBuffer [ i + 2 ] )
{
case 0x41 : currentKeyState [ 265 ] = 1 ; break ; // raylib KEY_UP
case 0x42 : currentKeyState [ 264 ] = 1 ; break ; // raylib KEY_DOWN
case 0x43 : currentKeyState [ 262 ] = 1 ; break ; // raylib KEY_RIGHT
case 0x44 : currentKeyState [ 263 ] = 1 ; break ; // raylib KEY_LEFT
default : break ;
}
2014-09-17 00:51:31 +04:00
2016-02-19 21:57:25 +03:00
i + = 3 ; // Jump to next key
}
2016-08-16 12:09:55 +03:00
2016-02-19 21:57:25 +03:00
// NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT)
2014-09-17 00:51:31 +04:00
}
}
}
2018-12-19 17:31:20 +03:00
else if ( keysBuffer [ i ] = = 0x0a ) { lastKeyPressed = 257 ; currentKeyState [ 257 ] = 1 ; } // raylib KEY_ENTER (don't mix with <linux/input.h> KEY_*)
else if ( keysBuffer [ i ] = = 0x7f ) { lastKeyPressed = 259 ; currentKeyState [ 259 ] = 1 ; } // raylib KEY_BACKSPACE
2016-02-19 21:57:25 +03:00
else
2014-09-17 00:51:31 +04:00
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_DEBUG , " Pressed key (ASCII): 0x%02x " , keysBuffer [ i ] ) ;
2016-08-16 12:09:55 +03:00
2016-02-19 21:57:25 +03:00
// Translate lowercase a-z letters to A-Z
if ( ( keysBuffer [ i ] > = 97 ) & & ( keysBuffer [ i ] < = 122 ) )
{
currentKeyState [ ( int ) keysBuffer [ i ] - 32 ] = 1 ;
}
else currentKeyState [ ( int ) keysBuffer [ i ] ] = 1 ;
2019-01-24 04:07:47 +03:00
2018-12-18 21:11:12 +03:00
lastKeyPressed = keysBuffer [ i ] ; // Register last key pressed
2014-09-17 00:51:31 +04:00
}
}
2016-08-16 12:09:55 +03:00
2016-02-19 21:57:25 +03:00
// Check exit key (same functionality as GLFW3 KeyCallback())
if ( currentKeyState [ exitKey ] = = 1 ) windowShouldClose = true ;
2016-08-16 12:09:55 +03:00
2018-04-29 12:37:39 +03:00
# if defined(SUPPORT_SCREEN_CAPTURE)
2017-05-11 17:24:40 +03:00
// Check screen capture key (raylib key: KEY_F12)
if ( currentKeyState [ 301 ] = = 1 )
{
TakeScreenshot ( FormatText ( " screenshot%03i.png " , screenshotCounter ) ) ;
screenshotCounter + + ;
}
2018-04-29 12:37:39 +03:00
# endif
2016-02-18 16:05:48 +03:00
}
2014-09-17 00:51:31 +04:00
2016-02-18 16:05:48 +03:00
// Restore default keyboard input
static void RestoreKeyboard ( void )
{
// Reset to default keyboard settings
tcsetattr ( STDIN_FILENO , TCSANOW , & defaultKeyboardSettings ) ;
2016-08-16 12:09:55 +03:00
2016-02-18 16:05:48 +03:00
// Reconfigure keyboard to default mode
ioctl ( STDIN_FILENO , KDSKBMODE , defaultKeyboardMode ) ;
2014-09-17 00:51:31 +04:00
}
2014-07-23 02:06:24 +04:00
2019-03-28 21:46:39 +03:00
// Initialise user input from evdev(/dev/input/event<N>) this means mouse, keyboard or gamepad devices
static void InitEvdevInput ( void )
2014-07-23 02:06:24 +04:00
{
2018-12-18 21:11:12 +03:00
char path [ MAX_FILEPATH_LENGTH ] ;
2018-10-22 12:48:16 +03:00
DIR * directory ;
struct dirent * entity ;
2018-10-21 02:09:17 +03:00
// Reset variables
for ( int i = 0 ; i < MAX_TOUCH_POINTS ; + + i )
{
touchPosition [ i ] . x = - 1 ;
touchPosition [ i ] . y = - 1 ;
}
2019-03-28 21:46:39 +03:00
// Reset keypress buffer
lastKeyPressedEvdev . Head = 0 ;
lastKeyPressedEvdev . Tail = 0 ;
// Reset keyboard key state
for ( int i = 0 ; i < 512 ; i + + ) currentKeyStateEvdev [ i ] = 0 ;
2018-10-21 02:09:17 +03:00
// Open the linux directory of "/dev/input"
2018-10-22 12:48:16 +03:00
directory = opendir ( DEFAULT_EVDEV_PATH ) ;
2018-11-06 17:10:50 +03:00
if ( directory )
2014-12-31 20:03:32 +03:00
{
2018-11-06 17:10:50 +03:00
while ( ( entity = readdir ( directory ) ) ! = NULL )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
if ( strncmp ( " event " , entity - > d_name , strlen ( " event " ) ) = = 0 ) // Search for devices named "event*"
2018-10-21 02:09:17 +03:00
{
2018-12-18 21:11:12 +03:00
sprintf ( path , " %s%s " , DEFAULT_EVDEV_PATH , entity - > d_name ) ;
EventThreadSpawn ( path ) ; // Identify the device and spawn a thread for it
2018-10-21 02:09:17 +03:00
}
}
2018-11-06 17:10:50 +03:00
2018-10-22 12:48:16 +03:00
closedir ( directory ) ;
2014-12-31 20:03:32 +03:00
}
2018-12-18 21:11:12 +03:00
else TraceLog ( LOG_WARNING , " Unable to open linux event directory: %s " , DEFAULT_EVDEV_PATH ) ;
2014-09-17 00:51:31 +04:00
}
2018-12-18 21:11:12 +03:00
// Identifies a input device and spawns a thread to handle it if needed
2018-10-22 12:48:16 +03:00
static void EventThreadSpawn ( char * device )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
# define BITS_PER_LONG (sizeof(long)*8)
# define NBITS(x) ((((x) - 1) / BITS_PER_LONG) + 1)
# define OFF(x) ((x)%BITS_PER_LONG)
# define BIT(x) (1UL<<OFF(x))
# define LONG(x) ((x) / BITS_PER_LONG)
2018-10-21 02:09:17 +03:00
# define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
2018-11-06 17:10:50 +03:00
2018-10-21 02:09:17 +03:00
struct input_absinfo absinfo ;
2018-10-22 12:48:16 +03:00
unsigned long evBits [ NBITS ( EV_MAX ) ] ;
unsigned long absBits [ NBITS ( ABS_MAX ) ] ;
unsigned long relBits [ NBITS ( REL_MAX ) ] ;
unsigned long keyBits [ NBITS ( KEY_MAX ) ] ;
2018-10-21 02:09:17 +03:00
bool hasAbs = false ;
bool hasRel = false ;
bool hasAbsMulti = false ;
2018-10-22 12:48:16 +03:00
int freeWorkerId = - 1 ;
2018-10-21 02:09:17 +03:00
int fd = - 1 ;
2018-11-06 17:10:50 +03:00
2018-10-22 12:48:16 +03:00
InputEventWorker * worker ;
2018-10-21 02:09:17 +03:00
2018-12-18 21:11:12 +03:00
// Open the device and allocate worker
//-------------------------------------------------------------------------------------------------------
2018-10-21 02:09:17 +03:00
// Find a free spot in the workers array
for ( int i = 0 ; i < sizeof ( eventWorkers ) / sizeof ( InputEventWorker ) ; + + i )
{
2018-10-22 12:48:16 +03:00
if ( eventWorkers [ i ] . threadId = = 0 )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
freeWorkerId = i ;
2018-10-21 02:09:17 +03:00
break ;
}
}
2016-08-16 12:09:55 +03:00
2018-10-21 02:09:17 +03:00
// Select the free worker from array
2018-10-22 12:48:16 +03:00
if ( freeWorkerId > = 0 )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
worker = & ( eventWorkers [ freeWorkerId ] ) ; // Grab a pointer to the worker
memset ( worker , 0 , sizeof ( InputEventWorker ) ) ; // Clear the worker
2018-10-21 02:09:17 +03:00
}
else
{
2018-12-18 21:11:12 +03:00
TraceLog ( LOG_WARNING , " Error creating input device thread for [%s]: Out of worker slots " , device ) ;
2018-10-21 02:09:17 +03:00
return ;
}
2014-09-17 00:51:31 +04:00
2018-10-21 02:09:17 +03:00
// Open the device
fd = open ( device , O_RDONLY | O_NONBLOCK ) ;
2018-10-22 12:48:16 +03:00
if ( fd < 0 )
2014-09-17 00:51:31 +04:00
{
2018-12-18 21:11:12 +03:00
TraceLog ( LOG_WARNING , " Error creating input device thread for [%s]: Can't open device (Error: %d) " , device , worker - > fd ) ;
2018-10-21 02:09:17 +03:00
return ;
}
2018-10-22 12:48:16 +03:00
worker - > fd = fd ;
2016-08-16 12:09:55 +03:00
2018-12-18 21:11:12 +03:00
// Grab number on the end of the devices name "event<N>"
2018-10-22 12:48:16 +03:00
int devNum = 0 ;
char * ptrDevName = strrchr ( device , ' t ' ) ;
worker - > eventNum = - 1 ;
2018-11-06 17:10:50 +03:00
2018-10-22 12:48:16 +03:00
if ( ptrDevName ! = NULL )
2018-10-21 17:06:40 +03:00
{
2018-10-22 12:48:16 +03:00
if ( sscanf ( ptrDevName , " t%d " , & devNum ) = = 1 )
worker - > eventNum = devNum ;
2018-10-21 17:06:40 +03:00
}
2018-12-18 21:11:12 +03:00
// 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...
//-------------------------------------------------------------------------------------------------------
2014-09-17 00:51:31 +04:00
2018-12-18 21:11:12 +03:00
// Identify the device
//-------------------------------------------------------------------------------------------------------
ioctl ( fd , EVIOCGBIT ( 0 , sizeof ( evBits ) ) , evBits ) ; // Read a bitfield of the avalable device properties
2018-10-21 02:09:17 +03:00
// Check for absolute input devices
2018-11-06 17:10:50 +03:00
if ( TEST_BIT ( evBits , EV_ABS ) )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
ioctl ( fd , EVIOCGBIT ( EV_ABS , sizeof ( absBits ) ) , absBits ) ;
2016-08-16 12:09:55 +03:00
2018-10-21 02:09:17 +03:00
// Check for absolute movement support (usualy touchscreens, but also joysticks)
2018-11-06 17:10:50 +03:00
if ( TEST_BIT ( absBits , ABS_X ) & & TEST_BIT ( absBits , ABS_Y ) )
2018-10-21 02:09:17 +03:00
{
hasAbs = true ;
2018-11-06 17:10:50 +03:00
2018-10-21 02:09:17 +03:00
// Get the scaling values
ioctl ( fd , EVIOCGABS ( ABS_X ) , & absinfo ) ;
2018-10-22 12:48:16 +03:00
worker - > absRange . x = absinfo . minimum ;
worker - > absRange . width = absinfo . maximum - absinfo . minimum ;
2018-10-21 02:09:17 +03:00
ioctl ( fd , EVIOCGABS ( ABS_Y ) , & absinfo ) ;
2018-10-22 12:48:16 +03:00
worker - > absRange . y = absinfo . minimum ;
worker - > absRange . height = absinfo . maximum - absinfo . minimum ;
2018-10-21 02:09:17 +03:00
}
2018-11-06 17:10:50 +03:00
2018-10-21 02:09:17 +03:00
// Check for multiple absolute movement support (usualy multitouch touchscreens)
2018-11-06 17:10:50 +03:00
if ( TEST_BIT ( absBits , ABS_MT_POSITION_X ) & & TEST_BIT ( absBits , ABS_MT_POSITION_Y ) )
2018-10-21 02:09:17 +03:00
{
hasAbsMulti = true ;
2018-11-06 17:10:50 +03:00
2018-10-21 02:09:17 +03:00
// Get the scaling values
ioctl ( fd , EVIOCGABS ( ABS_X ) , & absinfo ) ;
2018-10-22 12:48:16 +03:00
worker - > absRange . x = absinfo . minimum ;
worker - > absRange . width = absinfo . maximum - absinfo . minimum ;
2018-10-21 02:09:17 +03:00
ioctl ( fd , EVIOCGABS ( ABS_Y ) , & absinfo ) ;
2018-10-22 12:48:16 +03:00
worker - > absRange . y = absinfo . minimum ;
worker - > absRange . height = absinfo . maximum - absinfo . minimum ;
2018-10-21 02:09:17 +03:00
}
}
2016-08-16 12:09:55 +03:00
2018-10-21 02:09:17 +03:00
// Check for relative movement support (usualy mouse)
2018-11-06 17:10:50 +03:00
if ( TEST_BIT ( evBits , EV_REL ) )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
ioctl ( fd , EVIOCGBIT ( EV_REL , sizeof ( relBits ) ) , relBits ) ;
2018-11-06 17:10:50 +03:00
2018-10-22 12:48:16 +03:00
if ( TEST_BIT ( relBits , REL_X ) & & TEST_BIT ( relBits , REL_Y ) ) hasRel = true ;
2018-10-21 02:09:17 +03:00
}
2016-08-16 12:09:55 +03:00
2018-10-21 02:09:17 +03:00
// Check for button support to determine the device type(usualy on all input devices)
2018-11-06 17:10:50 +03:00
if ( TEST_BIT ( evBits , EV_KEY ) )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
ioctl ( fd , EVIOCGBIT ( EV_KEY , sizeof ( keyBits ) ) , keyBits ) ;
2016-02-19 21:57:25 +03:00
2018-10-22 12:48:16 +03:00
if ( hasAbs | | hasAbsMulti )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
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
2018-10-21 02:09:17 +03:00
}
2016-08-16 12:09:55 +03:00
2018-10-22 12:48:16 +03:00
if ( hasRel )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
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
2018-10-21 02:09:17 +03:00
}
2016-08-16 12:09:55 +03:00
2018-10-22 12:48:16 +03:00
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
2014-09-17 00:51:31 +04:00
}
2018-12-18 21:11:12 +03:00
//-------------------------------------------------------------------------------------------------------
2014-09-17 00:51:31 +04:00
2018-12-18 21:11:12 +03:00
// Decide what to do with the device
//-------------------------------------------------------------------------------------------------------
2019-03-28 21:46:39 +03:00
if ( worker - > isTouch | | worker - > isMouse | | worker - > isKeyboard )
2017-01-06 00:36:40 +03:00
{
2018-10-21 02:09:17 +03:00
// Looks like a interesting device
2018-12-18 21:11:12 +03:00
TraceLog ( LOG_INFO , " Opening input device [%s] (%s%s%s%s%s) " , device ,
2019-02-22 15:13:11 +03:00
worker - > isMouse ? " mouse " : " " ,
worker - > isMultitouch ? " multitouch " : " " ,
worker - > isTouch ? " touchscreen " : " " ,
worker - > isGamepad ? " gamepad " : " " ,
worker - > isKeyboard ? " keyboard " : " " ) ;
2018-10-21 17:06:40 +03:00
2018-10-21 02:09:17 +03:00
// Create a thread for this device
2018-10-22 12:48:16 +03:00
int error = pthread_create ( & worker - > threadId , NULL , & EventThread , ( void * ) worker ) ;
if ( error ! = 0 )
2018-10-21 02:09:17 +03:00
{
2018-12-18 21:11:12 +03:00
TraceLog ( LOG_WARNING , " Error creating input device thread for [%s]: Can't create thread (Error: %d) " , device , error ) ;
2018-10-22 12:48:16 +03:00
worker - > threadId = 0 ;
2018-10-21 02:09:17 +03:00
close ( fd ) ;
}
2018-10-21 17:06:40 +03:00
2018-10-22 12:48:16 +03:00
# if defined(USE_LAST_TOUCH_DEVICE)
// Find touchscreen with the highest index
int maxTouchNumber = - 1 ;
2018-11-06 17:10:50 +03:00
2018-10-22 12:48:16 +03:00
for ( int i = 0 ; i < sizeof ( eventWorkers ) / sizeof ( InputEventWorker ) ; + + i )
2018-10-21 17:06:40 +03:00
{
2018-10-22 12:48:16 +03:00
if ( eventWorkers [ i ] . isTouch & & ( eventWorkers [ i ] . eventNum > maxTouchNumber ) ) maxTouchNumber = eventWorkers [ i ] . eventNum ;
}
2018-11-06 17:10:50 +03:00
2018-10-22 12:48:16 +03:00
// Find toucnscreens with lower indexes
for ( int i = 0 ; i < sizeof ( eventWorkers ) / sizeof ( InputEventWorker ) ; + + i )
{
if ( eventWorkers [ i ] . isTouch & & ( eventWorkers [ i ] . eventNum < maxTouchNumber ) )
2018-10-21 17:06:40 +03:00
{
2018-10-22 12:48:16 +03:00
if ( eventWorkers [ i ] . threadId ! = 0 )
2018-10-21 17:06:40 +03:00
{
2018-12-18 21:11:12 +03:00
TraceLog ( LOG_WARNING , " Duplicate touchscreen found, killing touchscreen on event: %d " , i ) ;
2018-10-22 12:48:16 +03:00
pthread_cancel ( eventWorkers [ i ] . threadId ) ;
close ( eventWorkers [ i ] . fd ) ;
2018-10-21 17:06:40 +03:00
}
}
}
2018-10-22 12:48:16 +03:00
# endif
2017-01-06 00:36:40 +03:00
}
2018-10-22 12:48:16 +03:00
else close ( fd ) ; // We are not interested in this device
2018-12-18 21:11:12 +03:00
//-------------------------------------------------------------------------------------------------------
2017-01-06 00:36:40 +03:00
}
2018-12-18 21:11:12 +03:00
// Input device events reading thread
2018-10-21 02:09:17 +03:00
static void * EventThread ( void * arg )
2017-01-06 00:36:40 +03:00
{
2019-03-28 21:46:39 +03:00
// Scancode to keycode mapping for US keyboards
// TODO: Proabobly 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)
2019-04-04 14:50:52 +03:00
static const int keymap_US [ ] =
2019-03-28 21:46:39 +03:00
{ 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 , } ;
2018-10-22 12:48:16 +03:00
struct input_event event ;
InputEventWorker * worker = ( InputEventWorker * ) arg ;
2019-04-04 14:50:52 +03:00
2019-03-25 14:30:20 +03:00
int touchAction = - 1 ;
bool gestureUpdate = false ;
2019-03-28 21:46:39 +03:00
int keycode ;
2017-01-06 00:36:40 +03:00
while ( ! windowShouldClose )
{
2019-03-28 21:46:39 +03:00
// Try to read data from the device and only continue if successful
2018-10-22 12:48:16 +03:00
if ( read ( worker - > fd , & event , sizeof ( event ) ) = = ( int ) sizeof ( event ) )
2017-01-06 00:36:40 +03:00
{
2018-12-18 21:11:12 +03:00
// Relative movement parsing
2018-10-22 12:48:16 +03:00
if ( event . type = = EV_REL )
2017-01-14 19:18:06 +03:00
{
2018-10-22 12:48:16 +03:00
if ( event . code = = REL_X )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
mousePosition . x + = event . value ;
2018-10-21 02:09:17 +03:00
touchPosition [ 0 ] . x = mousePosition . x ;
2019-03-28 22:38:13 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
touchAction = TOUCH_MOVE ;
gestureUpdate = true ;
# endif
2018-10-21 02:09:17 +03:00
}
2018-10-22 12:48:16 +03:00
if ( event . code = = REL_Y )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
mousePosition . y + = event . value ;
2018-10-21 02:09:17 +03:00
touchPosition [ 0 ] . y = mousePosition . y ;
2019-03-28 22:38:13 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
touchAction = TOUCH_MOVE ;
gestureUpdate = true ;
# endif
2018-10-21 02:09:17 +03:00
}
2018-10-22 12:48:16 +03:00
if ( event . code = = REL_WHEEL )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
currentMouseWheelY + = event . value ;
2018-11-06 17:10:50 +03:00
}
2017-01-29 01:02:30 +03:00
}
2018-10-21 02:09:17 +03:00
2018-12-18 21:11:12 +03:00
// Absolute movement parsing
2018-10-22 12:48:16 +03:00
if ( event . type = = EV_ABS )
2017-01-14 19:18:06 +03:00
{
2018-10-21 02:09:17 +03:00
// Basic movement
2018-10-22 12:48:16 +03:00
if ( event . code = = ABS_X )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
mousePosition . x = ( event . value - worker - > absRange . x ) * screenWidth / worker - > absRange . width ; // Scale acording to absRange
2019-03-28 22:38:13 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
touchAction = TOUCH_MOVE ;
gestureUpdate = true ;
# endif
2018-10-21 02:09:17 +03:00
}
2018-10-22 12:48:16 +03:00
if ( event . code = = ABS_Y )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
mousePosition . y = ( event . value - worker - > absRange . y ) * screenHeight / worker - > absRange . height ; // Scale acording to absRange
2019-03-28 22:38:13 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
touchAction = TOUCH_MOVE ;
gestureUpdate = true ;
# endif
2018-10-21 02:09:17 +03:00
}
2018-10-22 12:48:16 +03:00
// Multitouch movement
if ( event . code = = ABS_MT_SLOT )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
worker - > touchSlot = event . value ; // Remeber the slot number for the folowing events
2018-10-21 02:09:17 +03:00
}
2018-10-22 12:48:16 +03:00
if ( event . code = = ABS_MT_POSITION_X )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
if ( worker - > touchSlot < MAX_TOUCH_POINTS )
touchPosition [ worker - > touchSlot ] . x = ( event . value - worker - > absRange . x ) * screenWidth / worker - > absRange . width ; // Scale acording to absRange
2018-10-21 02:09:17 +03:00
}
2018-10-22 12:48:16 +03:00
if ( event . code = = ABS_MT_POSITION_Y )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
if ( worker - > touchSlot < MAX_TOUCH_POINTS )
touchPosition [ worker - > touchSlot ] . y = ( event . value - worker - > absRange . y ) * screenHeight / worker - > absRange . height ; // Scale acording to absRange
2018-10-21 02:09:17 +03:00
}
2018-10-22 12:48:16 +03:00
if ( event . code = = ABS_MT_TRACKING_ID )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
if ( ( event . value < 0 ) & & ( worker - > touchSlot < MAX_TOUCH_POINTS ) )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
// Touch has ended for this point
touchPosition [ worker - > touchSlot ] . x = - 1 ;
touchPosition [ worker - > touchSlot ] . y = - 1 ;
2018-10-21 02:09:17 +03:00
}
}
2017-01-29 01:02:30 +03:00
}
2018-10-21 02:09:17 +03:00
2018-12-18 21:11:12 +03:00
// Button parsing
2018-10-22 12:48:16 +03:00
if ( event . type = = EV_KEY )
2017-01-14 19:18:06 +03:00
{
2019-03-28 21:46:39 +03:00
// Mouse button parsing
2019-03-01 01:06:37 +03:00
if ( ( event . code = = BTN_TOUCH ) | | ( event . code = = BTN_LEFT ) )
2018-10-21 02:09:17 +03:00
{
2018-10-22 12:48:16 +03:00
currentMouseStateEvdev [ MOUSE_LEFT_BUTTON ] = event . value ;
2019-04-04 14:50:52 +03:00
2019-03-28 22:38:13 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
if ( event . value > 0 ) touchAction = TOUCH_DOWN ;
else touchAction = TOUCH_UP ;
gestureUpdate = true ;
# endif
2018-10-21 02:09:17 +03:00
}
2018-10-22 12:48:16 +03:00
if ( event . code = = BTN_RIGHT ) currentMouseStateEvdev [ MOUSE_RIGHT_BUTTON ] = event . value ;
2018-10-21 02:09:17 +03:00
2018-10-22 12:48:16 +03:00
if ( event . code = = BTN_MIDDLE ) currentMouseStateEvdev [ MOUSE_MIDDLE_BUTTON ] = event . value ;
2019-03-28 21:46:39 +03:00
// Keyboard button parsing
if ( ( event . code > = 1 ) & & ( event . code < = 255 ) ) //Keyboard keys appear for codes 1 to 255
{
2019-04-04 14:50:52 +03:00
keycode = keymap_US [ event . code & 0xFF ] ; // The code we get is a scancode so we look up the apropriate keycode
2019-03-28 21:46:39 +03:00
// Make sure we got a valid keycode
2019-04-04 14:50:52 +03:00
if ( ( keycode > 0 ) & & ( keycode < sizeof ( currentKeyState ) ) )
2019-03-28 21:46:39 +03:00
{
// 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 ) ;
}
}
2017-01-14 19:18:06 +03:00
}
2018-10-21 02:09:17 +03:00
2018-12-18 21:11:12 +03:00
// Screen confinement
2018-10-22 12:48:16 +03:00
if ( mousePosition . x < 0 ) mousePosition . x = 0 ;
2019-01-02 13:14:55 +03:00
if ( mousePosition . x > screenWidth / mouseScale . x ) mousePosition . x = screenWidth / mouseScale . x ;
2018-10-21 02:09:17 +03:00
2018-10-22 12:48:16 +03:00
if ( mousePosition . y < 0 ) mousePosition . y = 0 ;
2019-01-02 22:09:34 +03:00
if ( mousePosition . y > screenHeight / mouseScale . y ) mousePosition . y = screenHeight / mouseScale . y ;
2018-10-21 02:09:17 +03:00
2018-12-18 21:11:12 +03:00
// Gesture update
2019-03-25 14:30:20 +03:00
if ( gestureUpdate )
2017-01-14 19:18:06 +03:00
{
2019-03-25 14:30:20 +03:00
# if defined(SUPPORT_GESTURES_SYSTEM)
GestureEvent gestureEvent = { 0 } ;
2018-10-21 02:09:17 +03:00
gestureEvent . pointCount = 0 ;
2019-03-25 14:30:20 +03:00
gestureEvent . touchAction = touchAction ;
2019-04-04 14:50:52 +03:00
2018-10-22 12:48:16 +03:00
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 + + ;
2019-04-04 14:50:52 +03:00
2017-01-14 19:18:06 +03:00
gestureEvent . pointerId [ 0 ] = 0 ;
gestureEvent . pointerId [ 1 ] = 1 ;
2018-10-21 02:09:17 +03:00
gestureEvent . pointerId [ 2 ] = 2 ;
gestureEvent . pointerId [ 3 ] = 3 ;
2019-04-04 14:50:52 +03:00
2018-10-21 02:09:17 +03:00
gestureEvent . position [ 0 ] = touchPosition [ 0 ] ;
gestureEvent . position [ 1 ] = touchPosition [ 1 ] ;
gestureEvent . position [ 2 ] = touchPosition [ 2 ] ;
gestureEvent . position [ 3 ] = touchPosition [ 3 ] ;
2019-04-04 14:50:52 +03:00
2017-01-14 19:18:06 +03:00
ProcessGestureEvent ( gestureEvent ) ;
2019-03-25 14:30:20 +03:00
# endif
2017-01-14 19:18:06 +03:00
}
2018-10-21 02:09:17 +03:00
}
else
{
2018-10-22 12:48:16 +03:00
usleep ( 5000 ) ; // Sleep for 5ms to avoid hogging CPU time
2017-01-06 00:36:40 +03:00
}
}
2018-11-06 17:10:50 +03:00
2018-10-22 12:48:16 +03:00
close ( worker - > fd ) ;
2018-11-06 17:10:50 +03:00
2017-01-06 00:36:40 +03:00
return NULL ;
}
2016-02-18 16:05:48 +03:00
// Init gamepad system
static void InitGamepad ( void )
2014-09-17 00:51:31 +04:00
{
2016-03-17 14:54:36 +03:00
char gamepadDev [ 128 ] = " " ;
2016-08-16 12:09:55 +03:00
2016-03-17 14:54:36 +03:00
for ( int i = 0 ; i < MAX_GAMEPADS ; i + + )
2014-09-17 00:51:31 +04:00
{
2016-03-17 14:54:36 +03:00
sprintf ( gamepadDev , " %s%i " , DEFAULT_GAMEPAD_DEV , i ) ;
2016-08-16 12:09:55 +03:00
if ( ( gamepadStream [ i ] = open ( gamepadDev , O_RDONLY | O_NONBLOCK ) ) < 0 )
2016-03-17 14:54:36 +03:00
{
// NOTE: Only show message for first gamepad
2017-07-02 13:35:13 +03:00
if ( i = = 0 ) TraceLog ( LOG_WARNING , " Gamepad device could not be opened, no gamepad available " ) ;
2016-03-17 14:54:36 +03:00
}
else
{
gamepadReady [ i ] = true ;
2014-09-17 00:51:31 +04:00
2016-03-17 14:54:36 +03:00
// NOTE: Only create one thread
if ( i = = 0 )
{
int error = pthread_create ( & gamepadThreadId , NULL , & GamepadThread , NULL ) ;
2014-09-17 00:51:31 +04:00
2017-07-02 13:35:13 +03:00
if ( error ! = 0 ) TraceLog ( LOG_WARNING , " Error creating gamepad input event thread " ) ;
else TraceLog ( LOG_INFO , " Gamepad device initialized successfully " ) ;
2016-03-17 14:54:36 +03:00
}
}
2016-08-16 12:09:55 +03:00
}
2014-09-17 00:51:31 +04:00
}
2016-02-18 16:05:48 +03:00
// Process Gamepad (/dev/input/js0)
static void * GamepadThread ( void * arg )
2014-09-17 00:51:31 +04:00
{
2016-02-18 16:05:48 +03:00
# 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
2016-05-31 20:12:37 +03:00
struct js_event gamepadEvent ;
2016-08-16 12:09:55 +03:00
2016-06-24 20:34:47 +03:00
while ( ! windowShouldClose )
2016-02-18 16:05:48 +03:00
{
2016-03-17 14:54:36 +03:00
for ( int i = 0 ; i < MAX_GAMEPADS ; i + + )
2016-02-18 16:05:48 +03:00
{
2016-03-17 14:54:36 +03:00
if ( read ( gamepadStream [ i ] , & gamepadEvent , sizeof ( struct js_event ) ) = = ( int ) sizeof ( struct js_event ) )
2016-02-18 16:05:48 +03:00
{
2016-03-17 14:54:36 +03:00
gamepadEvent . type & = ~ JS_EVENT_INIT ; // Ignore synthetic events
2016-08-16 12:09:55 +03:00
2016-03-17 14:54:36 +03:00
// Process gamepad events by type
2016-08-16 12:09:55 +03:00
if ( gamepadEvent . type = = JS_EVENT_BUTTON )
2016-02-18 16:05:48 +03:00
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_DEBUG , " Gamepad button: %i, value: %i " , gamepadEvent . number , gamepadEvent . value ) ;
2016-08-16 12:09:55 +03:00
if ( gamepadEvent . number < MAX_GAMEPAD_BUTTONS )
2016-03-17 14:54:36 +03:00
{
// 1 - button pressed, 0 - button released
2016-10-14 12:14:41 +03:00
currentGamepadState [ i ] [ gamepadEvent . number ] = ( int ) gamepadEvent . value ;
2017-01-29 01:02:30 +03:00
2016-10-27 14:41:43 +03:00
if ( ( int ) gamepadEvent . value = = 1 ) lastGamepadButtonPressed = gamepadEvent . number ;
2016-11-02 15:39:48 +03:00
else lastGamepadButtonPressed = - 1 ;
2016-03-17 14:54:36 +03:00
}
2016-02-18 16:05:48 +03:00
}
2016-08-16 12:09:55 +03:00
else if ( gamepadEvent . type = = JS_EVENT_AXIS )
2016-02-18 16:05:48 +03:00
{
2017-07-02 13:35:13 +03:00
TraceLog ( LOG_DEBUG , " Gamepad axis: %i, value: %i " , gamepadEvent . number , gamepadEvent . value ) ;
2016-08-16 12:09:55 +03:00
2016-03-17 14:54:36 +03:00
if ( gamepadEvent . number < MAX_GAMEPAD_AXIS )
{
// NOTE: Scaling of gamepadEvent.value to get values between -1..1
2016-10-14 12:14:41 +03:00
gamepadAxisState [ i ] [ gamepadEvent . number ] = ( float ) gamepadEvent . value / 32768 ;
2016-03-17 14:54:36 +03:00
}
2016-02-18 16:05:48 +03:00
}
}
2018-10-21 02:09:17 +03:00
else
{
usleep ( 1000 ) ; //Sleep for 1ms to avoid hogging CPU time
}
2016-02-18 16:05:48 +03:00
}
2016-05-31 20:12:37 +03:00
}
2016-02-19 21:57:25 +03:00
return NULL ;
2014-09-17 00:51:31 +04:00
}
2016-06-24 20:34:47 +03:00
# endif // PLATFORM_RPI
2014-09-17 00:51:31 +04:00
// Plays raylib logo appearing animation
static void LogoAnimation ( void )
{
2017-05-08 03:47:44 +03:00
# if !defined(PLATFORM_WEB)
2014-09-17 00:51:31 +04:00
int logoPositionX = screenWidth / 2 - 128 ;
int logoPositionY = screenHeight / 2 - 128 ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
int framesCounter = 0 ;
int lettersCount = 0 ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
int topSideRecWidth = 16 ;
int leftSideRecHeight = 16 ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
int bottomSideRecWidth = 16 ;
int rightSideRecHeight = 16 ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
int state = 0 ; // Tracking animation states (State Machine)
2014-09-17 00:51:31 +04:00
float alpha = 1.0f ; // Useful for fading
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
while ( ! WindowShouldClose ( ) & & ( state ! = 4 ) ) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if ( state = = 0 ) // State 0: Small box blinking
{
framesCounter + + ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
if ( framesCounter = = 84 )
2014-09-03 18:51:28 +04:00
{
2014-07-23 02:06:24 +04:00
state = 1 ;
framesCounter = 0 ; // Reset counter... will be used later...
}
}
else if ( state = = 1 ) // State 1: Top and left bars growing
{
topSideRecWidth + = 4 ;
leftSideRecHeight + = 4 ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
if ( topSideRecWidth = = 256 ) state = 2 ;
}
else if ( state = = 2 ) // State 2: Bottom and right bars growing
{
bottomSideRecWidth + = 4 ;
rightSideRecHeight + = 4 ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
if ( bottomSideRecWidth = = 256 ) state = 3 ;
}
else if ( state = = 3 ) // State 3: Letters appearing (one by one)
{
framesCounter + + ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
if ( framesCounter / 12 ) // Every 12 frames, one more letter!
2014-09-03 18:51:28 +04:00
{
2014-07-23 02:06:24 +04:00
lettersCount + + ;
framesCounter = 0 ;
}
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
if ( lettersCount > = 10 ) // When all letters have appeared, just fade out everything
{
2014-09-17 00:51:31 +04:00
alpha - = 0.02f ;
2014-09-03 18:51:28 +04:00
2014-09-17 00:51:31 +04:00
if ( alpha < = 0.0f )
2014-07-23 02:06:24 +04:00
{
2014-09-17 00:51:31 +04:00
alpha = 0.0f ;
2014-07-23 02:06:24 +04:00
state = 4 ;
}
}
}
//----------------------------------------------------------------------------------
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
// Draw
//----------------------------------------------------------------------------------
BeginDrawing ( ) ;
2014-09-03 18:51:28 +04:00
2014-09-17 00:51:31 +04:00
ClearBackground ( RAYWHITE ) ;
2014-07-23 02:06:24 +04:00
if ( state = = 0 )
{
if ( ( framesCounter / 12 ) % 2 ) DrawRectangle ( logoPositionX , logoPositionY , 16 , 16 , BLACK ) ;
}
else if ( state = = 1 )
{
DrawRectangle ( logoPositionX , logoPositionY , topSideRecWidth , 16 , BLACK ) ;
DrawRectangle ( logoPositionX , logoPositionY , 16 , leftSideRecHeight , BLACK ) ;
}
else if ( state = = 2 )
{
DrawRectangle ( logoPositionX , logoPositionY , topSideRecWidth , 16 , BLACK ) ;
DrawRectangle ( logoPositionX , logoPositionY , 16 , leftSideRecHeight , BLACK ) ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
DrawRectangle ( logoPositionX + 240 , logoPositionY , 16 , rightSideRecHeight , BLACK ) ;
DrawRectangle ( logoPositionX , logoPositionY + 240 , bottomSideRecWidth , 16 , BLACK ) ;
}
else if ( state = = 3 )
{
DrawRectangle ( logoPositionX , logoPositionY , topSideRecWidth , 16 , Fade ( BLACK , alpha ) ) ;
DrawRectangle ( logoPositionX , logoPositionY + 16 , 16 , leftSideRecHeight - 32 , Fade ( BLACK , alpha ) ) ;
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
DrawRectangle ( logoPositionX + 240 , logoPositionY + 16 , 16 , rightSideRecHeight - 32 , Fade ( BLACK , alpha ) ) ;
DrawRectangle ( logoPositionX , logoPositionY + 240 , bottomSideRecWidth , 16 , Fade ( BLACK , alpha ) ) ;
2014-09-03 18:51:28 +04:00
2014-09-17 00:51:31 +04:00
DrawRectangle ( screenWidth / 2 - 112 , screenHeight / 2 - 112 , 224 , 224 , Fade ( RAYWHITE , alpha ) ) ;
2014-09-03 18:51:28 +04:00
2018-12-26 15:26:34 +03:00
DrawText ( TextSubtext ( " raylib " , 0 , lettersCount ) , screenWidth / 2 - 44 , screenHeight / 2 + 48 , 50 , Fade ( BLACK , alpha ) ) ;
2014-07-23 02:06:24 +04:00
}
2014-09-03 18:51:28 +04:00
2014-07-23 02:06:24 +04:00
EndDrawing ( ) ;
//----------------------------------------------------------------------------------
}
2014-12-31 20:03:32 +03:00
# endif
2014-09-17 00:51:31 +04:00
showLogo = false ; // Prevent for repeating when reloading window (Android)
}