Wise&mystical  1.0
Project about Europe
Loading...
Searching...
No Matches
rcore.c
Go to the documentation of this file.
1
115#include "raylib.h" // Declares module functions
116
117// Check if config flags have been externally provided on compilation line
118#if !defined(EXTERNAL_CONFIG_FLAGS)
119 #include "config.h" // Defines module configuration flags
120#endif
121
122#include "utils.h" // Required for: TRACELOG() macros
123
124#define RLGL_IMPLEMENTATION
125#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
126
127#define RAYMATH_IMPLEMENTATION // Define external out-of-line implementation
128#include "raymath.h" // Vector3, Quaternion and Matrix functionality
129
130#if defined(SUPPORT_GESTURES_SYSTEM)
131 #define GESTURES_IMPLEMENTATION
132 #include "rgestures.h" // Gestures detection functionality
133#endif
134
135#if defined(SUPPORT_CAMERA_SYSTEM)
136 #define CAMERA_IMPLEMENTATION
137 #include "rcamera.h" // Camera system functionality
138#endif
139
140#if defined(SUPPORT_GIF_RECORDING)
141 #define MSF_GIF_MALLOC(contextPointer, newSize) RL_MALLOC(newSize)
142 #define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) RL_REALLOC(oldMemory, newSize)
143 #define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) RL_FREE(oldMemory)
144
145 #define MSF_GIF_IMPL
146 #include "external/msf_gif.h" // GIF recording functionality
147#endif
148
149#if defined(SUPPORT_COMPRESSION_API)
150 #define SINFL_IMPLEMENTATION
151 #define SINFL_NO_SIMD
152 #include "external/sinfl.h" // Deflate (RFC 1951) decompressor
153
154 #define SDEFL_IMPLEMENTATION
155 #include "external/sdefl.h" // Deflate (RFC 1951) compressor
156#endif
157
158#if (defined(__linux__) || defined(PLATFORM_WEB)) && _POSIX_C_SOURCE < 199309L
159 #undef _POSIX_C_SOURCE
160 #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
161#endif
162
163// Platform specific defines to handle GetApplicationDirectory()
164#if defined (PLATFORM_DESKTOP)
165 #if defined(_WIN32)
166 #ifndef MAX_PATH
167 #define MAX_PATH 1025
168 #endif
169 __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize);
170 __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize);
171 __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default);
172 #elif defined(__linux__)
173 #include <unistd.h>
174 #elif defined(__APPLE__)
175 #include <sys/syslimits.h>
176 #include <mach-o/dyld.h>
177 #endif // OSs
178#endif // PLATFORM_DESKTOP
179
180#include <stdlib.h> // Required for: srand(), rand(), atexit()
181#include <stdio.h> // Required for: sprintf() [Used in OpenURL()]
182#include <string.h> // Required for: strrchr(), strcmp(), strlen(), memset()
183#include <time.h> // Required for: time() [Used in InitTimer()]
184#include <math.h> // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
185
186#include <sys/stat.h> // Required for: stat() [Used in GetFileModTime()]
187
188#if defined(PLATFORM_DESKTOP) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
189 #define DIRENT_MALLOC RL_MALLOC
190 #define DIRENT_FREE RL_FREE
191
192 #include "external/dirent.h" // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()]
193#else
194 #include <dirent.h> // Required for: DIR, opendir(), closedir() [Used in GetDirectoryFiles()]
195#endif
196
197#if defined(_WIN32)
198 #include <direct.h> // Required for: _getch(), _chdir()
199 #define GETCWD _getcwd // NOTE: MSDN recommends not to use getcwd(), chdir()
200 #define CHDIR _chdir
201 #include <io.h> // Required for: _access() [Used in FileExists()]
202#else
203 #include <unistd.h> // Required for: getch(), chdir() (POSIX), access()
204 #define GETCWD getcwd
205 #define CHDIR chdir
206#endif
207
208#if defined(PLATFORM_DESKTOP)
209 #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3
210 // NOTE: Already provided by rlgl implementation (on glad.h)
211 #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management
212 // NOTE: GLFW3 already includes gl.h (OpenGL) headers
213
214 // Support retrieving native window handlers
215 #if defined(_WIN32)
216 #define GLFW_EXPOSE_NATIVE_WIN32
217 #include "GLFW/glfw3native.h" // WARNING: It requires customization to avoid windows.h inclusion!
218
219 #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
220 // NOTE: Those functions require linking with winmm library
221 unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
222 unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
223 #endif
224 #endif
225 #if defined(__linux__) || defined(__FreeBSD__)
226 #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
227
228 //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
229 //#define GLFW_EXPOSE_NATIVE_WAYLAND
230 //#define GLFW_EXPOSE_NATIVE_MIR
231 #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window()
232 #endif
233 #if defined(__APPLE__)
234 #include <unistd.h> // Required for: usleep()
235
236 //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition
237 #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow()
238 #endif
239#endif
240
241#if defined(PLATFORM_ANDROID)
242 //#include <android/sensor.h> // Required for: Android sensors functions (accelerometer, gyroscope, light...)
243 #include <android/window.h> // Required for: AWINDOW_FLAG_FULLSCREEN definition and others
244 #include <android_native_app_glue.h> // Required for: android_app struct and activity management
245 #include <jni.h> // Required for: JNIEnv and JavaVM [Used in OpenURL()]
246
247 #include <EGL/egl.h> // Native platform windowing system interface
248 //#include <GLES2/gl2.h> // OpenGL ES 2.0 library (not required in this module, only in rlgl)
249#endif
250
251#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
252 #include <fcntl.h> // POSIX file control definitions - open(), creat(), fcntl()
253 #include <unistd.h> // POSIX standard function definitions - read(), close(), STDIN_FILENO
254 #include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
255 #include <pthread.h> // POSIX threads management (inputs reading)
256 #include <dirent.h> // POSIX directory browsing
257
258 #include <sys/ioctl.h> // Required for: ioctl() - UNIX System call for device-specific input/output operations
259 #include <linux/kd.h> // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
260 #include <linux/input.h> // Linux: Keycodes constants definition (KEY_A, ...)
261 #include <linux/joystick.h> // Linux: Joystick support library
262
263#if defined(PLATFORM_RPI)
264 #include "bcm_host.h" // Raspberry Pi VideoCore IV access functions
265#endif
266#if defined(PLATFORM_DRM)
267 #include <gbm.h> // Generic Buffer Management (native platform for EGL on DRM)
268 #include <xf86drm.h> // Direct Rendering Manager user-level library interface
269 #include <xf86drmMode.h> // Direct Rendering Manager mode setting (KMS) interface
270#endif
271
272 #include "EGL/egl.h" // Native platform windowing system interface
273 #include "EGL/eglext.h" // EGL extensions
274 //#include "GLES2/gl2.h" // OpenGL ES 2.0 library (not required in this module, only in rlgl)
275#endif
276
277#if defined(PLATFORM_WEB)
278 #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL)
279 #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management
280 #include <sys/time.h> // Required for: timespec, nanosleep(), select() - POSIX
281
282 #include <emscripten/emscripten.h> // Emscripten functionality for C
283 #include <emscripten/html5.h> // Emscripten HTML5 library
284#endif
285
286//----------------------------------------------------------------------------------
287// Defines and Macros
288//----------------------------------------------------------------------------------
289#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
290 #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event<N> number
291
292 #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...)
293 #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events
294#endif
295
296#ifndef MAX_FILEPATH_LENGTH
297 #if defined(__linux__)
298 #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value)
299 #else
300 #define MAX_FILEPATH_LENGTH 512 // Maximum length supported for filepaths
301 #endif
302#endif
303
304#ifndef MAX_KEYBOARD_KEYS
305 #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported
306#endif
307#ifndef MAX_MOUSE_BUTTONS
308 #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported
309#endif
310#ifndef MAX_GAMEPADS
311 #define MAX_GAMEPADS 4 // Maximum number of gamepads supported
312#endif
313#ifndef MAX_GAMEPAD_AXIS
314 #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad)
315#endif
316#ifndef MAX_GAMEPAD_BUTTONS
317 #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad)
318#endif
319#ifndef MAX_TOUCH_POINTS
320 #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported
321#endif
322#ifndef MAX_KEY_PRESSED_QUEUE
323 #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue
324#endif
325#ifndef MAX_CHAR_PRESSED_QUEUE
326 #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue
327#endif
328
329#if defined(SUPPORT_DATA_STORAGE)
330 #ifndef STORAGE_DATA_FILE
331 #define STORAGE_DATA_FILE "storage.data" // Automatic storage filename
332 #endif
333#endif
334
335#ifndef MAX_DECOMPRESSION_SIZE
336 #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB
337#endif
338
339// Flags operation macros
340#define FLAG_SET(n, f) ((n) |= (f))
341#define FLAG_CLEAR(n, f) ((n) &= ~(f))
342#define FLAG_TOGGLE(n, f) ((n) ^= (f))
343#define FLAG_CHECK(n, f) ((n) & (f))
344
345//----------------------------------------------------------------------------------
346// Types and Structures Definition
347//----------------------------------------------------------------------------------
348#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
349typedef struct {
350 pthread_t threadId; // Event reading thread id
351 int fd; // File descriptor to the device it is assigned to
352 int eventNum; // Number of 'event<N>' device
353 Rectangle absRange; // Range of values for absolute pointing devices (touchscreens)
354 int touchSlot; // Hold the touch slot number of the currently being sent multitouch block
355 bool isMouse; // True if device supports relative X Y movements
356 bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH
357 bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH
358 bool isKeyboard; // True if device has letter keycodes
359 bool isGamepad; // True if device has gamepad buttons
360} InputEventWorker;
361#endif
362
363typedef struct { int x; int y; } Point;
364typedef struct { unsigned int width; unsigned int height; } Size;
365
366// Core global state context data
367typedef struct CoreData {
368 struct {
369#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
370 GLFWwindow *handle; // GLFW window handle (graphic device)
371#endif
372#if defined(PLATFORM_RPI)
373 EGL_DISPMANX_WINDOW_T handle; // Native window handle (graphic device)
374#endif
375#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
376#if defined(PLATFORM_DRM)
377 int fd; // File descriptor for /dev/dri/...
378 drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector
379 drmModeCrtc *crtc; // CRT Controller
380 int modeIndex; // Index of the used mode of connector->modes
381 struct gbm_device *gbmDevice; // GBM device
382 struct gbm_surface *gbmSurface; // GBM surface
383 struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping)
384 uint32_t prevFB; // Previous GBM framebufer (during frame swapping)
385#endif // PLATFORM_DRM
386 EGLDisplay device; // Native display device (physical screen connection)
387 EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
388 EGLContext context; // Graphic context, mode in which drawing can be done
389 EGLConfig config; // Graphic config
390#endif
391 const char *title; // Window text title const pointer
392 unsigned int flags; // Configuration flags (bit based), keeps window state
393 bool ready; // Check if window has been initialized successfully
394 bool fullscreen; // Check if fullscreen mode is enabled
395 bool shouldClose; // Check if window set for closing
396 bool resizedLastFrame; // Check if window has been resized last frame
397
398 Point position; // Window position on screen (required on fullscreen toggle)
399 Size display; // Display width and height (monitor, device-screen, LCD, ...)
400 Size screen; // Screen width and height (used render area)
401 Size currentFbo; // Current render width and height (depends on active fbo)
402 Size render; // Framebuffer width and height (render area, including black bars if required)
403 Point renderOffset; // Offset from render area (must be divided by 2)
404 Matrix screenScale; // Matrix to scale screen (framebuffer rendering)
405
406 char **dropFilesPath; // Store dropped files paths as strings
407 int dropFileCount; // Count dropped files strings
408
410#if defined(PLATFORM_ANDROID)
411 struct {
412 bool appEnabled; // Flag to detect if app is active ** = true
413 struct android_app *app; // Android activity
414 struct android_poll_source *source; // Android events polling source
415 bool contextRebindRequired; // Used to know context rebind required
416 } Android;
417#endif
418 struct {
419 const char *basePath; // Base path for data storage
421 struct {
422#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
423 InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event<N>"
424#endif
425 struct {
426 int exitKey; // Default exit key
427 char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state
428 char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state
429
430 int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue
431 int keyPressedQueueCount; // Input keys queue count
432
433 int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode)
434 int charPressedQueueCount; // Input characters queue count
435
436#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
437 int defaultMode; // Default keyboard mode
438#if defined(SUPPORT_SSH_KEYBOARD_RPI)
439 bool evtMode; // Keyboard in event mode
440#endif
441 int defaultFileFlags; // Default IO file flags
442 struct termios defaultSettings; // Default keyboard settings
443 int fd; // File descriptor for the evdev keyboard
444#endif
446 struct {
447 Vector2 offset; // Mouse offset
448 Vector2 scale; // Mouse scaling
449 Vector2 currentPosition; // Mouse position on screen
450 Vector2 previousPosition; // Previous mouse position
451
452 int cursor; // Tracks current mouse cursor
453 bool cursorHidden; // Track if cursor is hidden
454 bool cursorOnScreen; // Tracks if cursor is inside client area
455
456 char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state
457 char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state
458 float currentWheelMove; // Registers current mouse wheel variation
459 float previousWheelMove; // Registers previous mouse wheel variation
460#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
461 // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update
462 char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab
463#endif
465 struct {
466 int pointCount; // Number of touch points active
467 int pointId[MAX_TOUCH_POINTS]; // Point identifiers
468 Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen
469 char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state
470 char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state
472 struct {
473 int lastButtonPressed; // Register last gamepad button pressed
474 int axisCount; // Register number of available gamepad axis
475 bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready
476 char name[MAX_GAMEPADS][64]; // Gamepad name holder
477 char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state
478 char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state
479 float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
480#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
481 pthread_t threadId; // Gamepad reading thread id
482 int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor
483#endif
486 struct {
487 double current; // Current time measure
488 double previous; // Previous time measure
489 double update; // Time measure for frame update
490 double draw; // Time measure for frame draw
491 double frame; // Time measure for one frame
492 double target; // Desired time for one frame, if 0 not applied
493#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
494 unsigned long long base; // Base time measure for hi-res timer
495#endif
496 unsigned int frameCounter; // Frame counter
499
500//----------------------------------------------------------------------------------
501// Global Variables Definition
502//----------------------------------------------------------------------------------
503static CoreData CORE = { 0 }; // Global CORE state context
504
505static char **dirFilesPath = NULL; // Store directory files paths as strings
506static int dirFileCount = 0; // Count directory files strings
507
508const char *raylibVersion = RAYLIB_VERSION; // raylib version symbol, it could be required for some bindings
509
510#if defined(SUPPORT_SCREEN_CAPTURE)
511static int screenshotCounter = 0; // Screenshots counter
512#endif
513
514#if defined(SUPPORT_GIF_RECORDING)
515static int gifFrameCounter = 0; // GIF frames counter
516static bool gifRecording = false; // GIF recording state
517static MsfGifState gifState = { 0 }; // MSGIF context state
518#endif
519
520#if defined(SUPPORT_EVENTS_AUTOMATION)
521#define MAX_CODE_AUTOMATION_EVENTS 16384
522
523typedef enum AutomationEventType {
524 EVENT_NONE = 0,
525 // Input events
526 INPUT_KEY_UP, // param[0]: key
527 INPUT_KEY_DOWN, // param[0]: key
528 INPUT_KEY_PRESSED, // param[0]: key
529 INPUT_KEY_RELEASED, // param[0]: key
530 INPUT_MOUSE_BUTTON_UP, // param[0]: button
531 INPUT_MOUSE_BUTTON_DOWN, // param[0]: button
532 INPUT_MOUSE_POSITION, // param[0]: x, param[1]: y
533 INPUT_MOUSE_WHEEL_MOTION, // param[0]: delta
534 INPUT_GAMEPAD_CONNECT, // param[0]: gamepad
535 INPUT_GAMEPAD_DISCONNECT, // param[0]: gamepad
536 INPUT_GAMEPAD_BUTTON_UP, // param[0]: button
537 INPUT_GAMEPAD_BUTTON_DOWN, // param[0]: button
538 INPUT_GAMEPAD_AXIS_MOTION, // param[0]: axis, param[1]: delta
539 INPUT_TOUCH_UP, // param[0]: id
540 INPUT_TOUCH_DOWN, // param[0]: id
541 INPUT_TOUCH_POSITION, // param[0]: x, param[1]: y
542 INPUT_GESTURE, // param[0]: gesture
543 // Window events
544 WINDOW_CLOSE, // no params
545 WINDOW_MAXIMIZE, // no params
546 WINDOW_MINIMIZE, // no params
547 WINDOW_RESIZE, // param[0]: width, param[1]: height
548 // Custom events
549 ACTION_TAKE_SCREENSHOT,
550 ACTION_SETTARGETFPS
551} AutomationEventType;
552
553// Event type
554// Used to enable events flags
555typedef enum {
556 EVENT_INPUT_KEYBOARD = 0,
557 EVENT_INPUT_MOUSE = 1,
558 EVENT_INPUT_GAMEPAD = 2,
559 EVENT_INPUT_TOUCH = 4,
560 EVENT_INPUT_GESTURE = 8,
561 EVENT_WINDOW = 16,
562 EVENT_CUSTOM = 32
563} EventType;
564
565static const char *autoEventTypeName[] = {
566 "EVENT_NONE",
567 "INPUT_KEY_UP",
568 "INPUT_KEY_DOWN",
569 "INPUT_KEY_PRESSED",
570 "INPUT_KEY_RELEASED",
571 "INPUT_MOUSE_BUTTON_UP",
572 "INPUT_MOUSE_BUTTON_DOWN",
573 "INPUT_MOUSE_POSITION",
574 "INPUT_MOUSE_WHEEL_MOTION",
575 "INPUT_GAMEPAD_CONNECT",
576 "INPUT_GAMEPAD_DISCONNECT",
577 "INPUT_GAMEPAD_BUTTON_UP",
578 "INPUT_GAMEPAD_BUTTON_DOWN",
579 "INPUT_GAMEPAD_AXIS_MOTION",
580 "INPUT_TOUCH_UP",
581 "INPUT_TOUCH_DOWN",
582 "INPUT_TOUCH_POSITION",
583 "INPUT_GESTURE",
584 "WINDOW_CLOSE",
585 "WINDOW_MAXIMIZE",
586 "WINDOW_MINIMIZE",
587 "WINDOW_RESIZE",
588 "ACTION_TAKE_SCREENSHOT",
589 "ACTION_SETTARGETFPS"
590};
591
592// Automation Event (20 bytes)
593typedef struct AutomationEvent {
594 unsigned int frame; // Event frame
595 unsigned int type; // Event type (AutoEventType)
596 int params[3]; // Event parameters (if required)
597} AutomationEvent;
598
599static AutomationEvent *events = NULL; // Events array
600static unsigned int eventCount = 0; // Events count
601static bool eventsPlaying = false; // Play events
602static bool eventsRecording = false; // Record events
603
604//static short eventsEnabled = 0b0000001111111111; // Events enabled for checking
605#endif
606//-----------------------------------------------------------------------------------
607
608//----------------------------------------------------------------------------------
609// Other Modules Functions Declaration (required by core)
610//----------------------------------------------------------------------------------
611#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
612extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow()
613extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory
614#endif
615
616//----------------------------------------------------------------------------------
617// Module specific Functions Declaration
618//----------------------------------------------------------------------------------
619static void InitTimer(void); // Initialize timer (hi-resolution if available)
620static bool InitGraphicsDevice(int width, int height); // Initialize graphics device
621static void SetupFramebuffer(int width, int height); // Setup main framebuffer
622static void SetupViewport(int width, int height); // Set viewport for a provided width and height
623
624#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
625static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
626// Window callbacks events
627static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized
628#if !defined(PLATFORM_WEB)
629static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized
630#endif
631static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored
632static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus
633static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window
634// Input callbacks events
635static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
636static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value)
637static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed
638static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move
639static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel
640static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area
641#endif
642
643#if defined(PLATFORM_ANDROID)
644static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands
645static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs
646#endif
647
648#if defined(PLATFORM_WEB)
649static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
650static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
651static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
652
653static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
654static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
655static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
656#endif
657
658#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
659static void InitKeyboard(void); // Initialize raw keyboard system
660static void RestoreKeyboard(void); // Restore keyboard system
661#if defined(SUPPORT_SSH_KEYBOARD_RPI)
662static void ProcessKeyboard(void); // Process keyboard events
663#endif
664
665static void InitEvdevInput(void); // Initialize evdev inputs
666static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate
667static void PollKeyboardEvents(void); // Process evdev keyboard events.
668static void *EventThread(void *arg); // Input device events reading thread
669
670static void InitGamepad(void); // Initialize raw gamepad input
671static void *GamepadThread(void *arg); // Mouse reading thread
672
673#if defined(PLATFORM_DRM)
674static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list
675static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list
676static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search the nearest matching DRM connector mode in connector's list
677#endif
678
679#endif // PLATFORM_RPI || PLATFORM_DRM
680
681#if defined(SUPPORT_EVENTS_AUTOMATION)
682static void LoadAutomationEvents(const char *fileName); // Load automation events from file
683static void ExportAutomationEvents(const char *fileName); // Export recorded automation events into a file
684static void RecordAutomationEvent(unsigned int frame); // Record frame events (to internal events array)
685static void PlayAutomationEvent(unsigned int frame); // Play frame events (from internal events array)
686#endif
687
688#if defined(_WIN32)
689// NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
690void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime()
691#endif
692
693#if !defined(SUPPORT_MODULE_RTEXT)
694const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
695#endif // !SUPPORT_MODULE_RTEXT
696
697//----------------------------------------------------------------------------------
698// Module Functions Definition - Window and OpenGL Context Functions
699//----------------------------------------------------------------------------------
700#if defined(PLATFORM_ANDROID)
701// To allow easier porting to android, we allow the user to define a
702// main function which we call from android_main, defined by ourselves
703extern int main(int argc, char *argv[]);
704
705void android_main(struct android_app *app)
706{
707 char arg0[] = "raylib"; // NOTE: argv[] are mutable
708 CORE.Android.app = app;
709
710 // NOTE: Return codes != 0 are skipped
711 (void)main(1, (char *[]) { arg0, NULL });
712}
713
714// NOTE: Add this to header (if apps really need it)
715struct android_app *GetAndroidApp(void)
716{
717 return CORE.Android.app;
718}
719#endif
720
721// Initialize window and OpenGL context
722// NOTE: data parameter could be used to pass any kind of required data to the initialization
723void InitWindow(int width, int height, const char *title)
724{
725 TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
726
727 TRACELOG(LOG_INFO, "Supported raylib modules:");
728 TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)");
729 TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)");
730#if defined(SUPPORT_MODULE_RSHAPES)
731 TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)");
732#else
733 TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)");
734#endif
735#if defined(SUPPORT_MODULE_RTEXTURES)
736 TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)");
737#else
738 TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)");
739#endif
740#if defined(SUPPORT_MODULE_RTEXT)
741 TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)");
742#else
743 TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)");
744#endif
745#if defined(SUPPORT_MODULE_RMODELS)
746 TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)");
747#else
748 TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)");
749#endif
750#if defined(SUPPORT_MODULE_RAUDIO)
751 TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)");
752#else
753 TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)");
754#endif
755
756 if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
757
758 // Initialize global input state
759 memset(&CORE.Input, 0, sizeof(CORE.Input));
761 CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
764
765#if defined(PLATFORM_ANDROID)
766 CORE.Window.screen.width = width;
767 CORE.Window.screen.height = height;
768 CORE.Window.currentFbo.width = width;
769 CORE.Window.currentFbo.height = height;
770
771 // Set desired windows flags before initializing anything
772 ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER
773
774 int orientation = AConfiguration_getOrientation(CORE.Android.app->config);
775
776 if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait");
777 else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape");
778
779 // TODO: Automatic orientation doesn't seem to work
780 if (width <= height)
781 {
782 AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT);
783 TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait");
784 }
785 else
786 {
787 AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND);
788 TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape");
789 }
790
791 //AConfiguration_getDensity(CORE.Android.app->config);
792 //AConfiguration_getKeyboard(CORE.Android.app->config);
793 //AConfiguration_getScreenSize(CORE.Android.app->config);
794 //AConfiguration_getScreenLong(CORE.Android.app->config);
795
796 // Initialize App command system
797 // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()...
798 CORE.Android.app->onAppCmd = AndroidCommandCallback;
799
800 // Initialize input events system
801 CORE.Android.app->onInputEvent = AndroidInputCallback;
802
803 // Initialize assets manager
804 InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath);
805
806 // Initialize base path for storage
807 CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath;
808
809 TRACELOG(LOG_INFO, "ANDROID: App initialized successfully");
810
811 // Android ALooper_pollAll() variables
812 int pollResult = 0;
813 int pollEvents = 0;
814
815 // Wait for window to be initialized (display and context)
816 while (!CORE.Window.ready)
817 {
818 // Process events loop
819 while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0)
820 {
821 // Process this event
822 if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source);
823
824 // NOTE: Never close window, native activity is controlled by the system!
825 //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true;
826 }
827 }
828#endif
829#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
830 // Initialize graphics device (display device and OpenGL context)
831 // NOTE: returns true if window and graphic device has been initialized successfully
832 CORE.Window.ready = InitGraphicsDevice(width, height);
833
834 // If graphic device is no properly initialized, we end program
835 if (!CORE.Window.ready)
836 {
837 TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device");
838 return;
839 }
840
841 // Initialize hi-res timer
842 InitTimer();
843
844 // Initialize random seed
845 srand((unsigned int)time(NULL));
846
847 // Initialize base path for storage
849
850#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
851 // Load default font
852 // WARNING: External function: Module required: rtext
854 #if defined(SUPPORT_MODULE_RSHAPES)
855 Rectangle rec = GetFontDefault().recs[95];
856 // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
857 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes
858 #endif
859#else
860 #if defined(SUPPORT_MODULE_RSHAPES)
861 // Set default texture and rectangle to be used for shapes drawing
862 // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
864 SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes
865 #endif
866#endif
867#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
868 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
869 {
870 // Set default font texture filter for HighDPI (blurry)
871 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
874 }
875#endif
876
877#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
878 // Initialize raw input system
879 InitEvdevInput(); // Evdev inputs initialization
880 InitGamepad(); // Gamepad init
881 InitKeyboard(); // Keyboard init (stdin)
882#endif
883
884#if defined(PLATFORM_WEB)
885 // Setup callback funtions for the DOM events
886 emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback);
887
888 // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
889 // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
890 //emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
891 // Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
892 //emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
893 // Trigger this once to get initial window sizing
894 //EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
895
896 // Support keyboard events -> Not used, GLFW.JS takes care of that
897 //emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
898 //emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
899
900 // Support mouse events
901 emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
902
903 // Support touch events
904 emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
905 emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
906 emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
907 emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
908
909 // Support gamepad events (not provided by GLFW3 on emscripten)
910 emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
911 emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
912#endif
913
914 CORE.Input.Mouse.currentPosition.x = (float)CORE.Window.screen.width/2.0f;
915 CORE.Input.Mouse.currentPosition.y = (float)CORE.Window.screen.height/2.0f;
916
917#if defined(SUPPORT_EVENTS_AUTOMATION)
918 events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent));
919 CORE.Time.frameCounter = 0;
920#endif
921
922#endif // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_RPI || PLATFORM_DRM
923}
924
925// Close window and unload OpenGL context
926void CloseWindow(void)
927{
928#if defined(SUPPORT_GIF_RECORDING)
929 if (gifRecording)
930 {
931 MsfGifResult result = msf_gif_end(&gifState);
932 msf_gif_free(result);
933 gifRecording = false;
934 }
935#endif
936
937#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
938 UnloadFontDefault(); // WARNING: Module required: rtext
939#endif
940
941 rlglClose(); // De-init rlgl
942
943#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
944 glfwDestroyWindow(CORE.Window.handle);
946#endif
947
948#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
949 timeEndPeriod(1); // Restore time period
950#endif
951
952#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
953 // Close surface, context and display
954 if (CORE.Window.device != EGL_NO_DISPLAY)
955 {
957
958 if (CORE.Window.surface != EGL_NO_SURFACE)
959 {
960 eglDestroySurface(CORE.Window.device, CORE.Window.surface);
961 CORE.Window.surface = EGL_NO_SURFACE;
962 }
963
964 if (CORE.Window.context != EGL_NO_CONTEXT)
965 {
966 eglDestroyContext(CORE.Window.device, CORE.Window.context);
967 CORE.Window.context = EGL_NO_CONTEXT;
968 }
969
970 eglTerminate(CORE.Window.device);
971 CORE.Window.device = EGL_NO_DISPLAY;
972 }
973#endif
974
975#if defined(PLATFORM_DRM)
976 if (CORE.Window.prevFB)
977 {
978 drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
979 CORE.Window.prevFB = 0;
980 }
981
982 if (CORE.Window.prevBO)
983 {
984 gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
985 CORE.Window.prevBO = NULL;
986 }
987
988 if (CORE.Window.gbmSurface)
989 {
990 gbm_surface_destroy(CORE.Window.gbmSurface);
991 CORE.Window.gbmSurface = NULL;
992 }
993
994 if (CORE.Window.gbmDevice)
995 {
996 gbm_device_destroy(CORE.Window.gbmDevice);
997 CORE.Window.gbmDevice = NULL;
998 }
999
1000 if (CORE.Window.crtc)
1001 {
1002 if (CORE.Window.connector)
1003 {
1004 drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id,
1005 CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode);
1006 drmModeFreeConnector(CORE.Window.connector);
1007 CORE.Window.connector = NULL;
1008 }
1009
1010 drmModeFreeCrtc(CORE.Window.crtc);
1011 CORE.Window.crtc = NULL;
1012 }
1013
1014 if (CORE.Window.fd != -1)
1015 {
1016 close(CORE.Window.fd);
1017 CORE.Window.fd = -1;
1018 }
1019
1020 // Close surface, context and display
1021 if (CORE.Window.device != EGL_NO_DISPLAY)
1022 {
1023 if (CORE.Window.surface != EGL_NO_SURFACE)
1024 {
1025 eglDestroySurface(CORE.Window.device, CORE.Window.surface);
1026 CORE.Window.surface = EGL_NO_SURFACE;
1027 }
1028
1029 if (CORE.Window.context != EGL_NO_CONTEXT)
1030 {
1031 eglDestroyContext(CORE.Window.device, CORE.Window.context);
1032 CORE.Window.context = EGL_NO_CONTEXT;
1033 }
1034
1035 eglTerminate(CORE.Window.device);
1036 CORE.Window.device = EGL_NO_DISPLAY;
1037 }
1038#endif
1039
1040#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
1041 // Wait for mouse and gamepad threads to finish before closing
1042 // NOTE: Those threads should already have finished at this point
1043 // because they are controlled by CORE.Window.shouldClose variable
1044
1045 CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called
1046
1047 // Close the evdev keyboard
1048 if (CORE.Input.Keyboard.fd != -1)
1049 {
1050 close(CORE.Input.Keyboard.fd);
1051 CORE.Input.Keyboard.fd = -1;
1052 }
1053
1054 for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
1055 {
1056 if (CORE.Input.eventWorker[i].threadId)
1057 {
1058 pthread_join(CORE.Input.eventWorker[i].threadId, NULL);
1059 }
1060 }
1061
1062 if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL);
1063#endif
1064
1065#if defined(SUPPORT_EVENTS_AUTOMATION)
1066 free(events);
1067#endif
1068
1069 CORE.Window.ready = false;
1070 TRACELOG(LOG_INFO, "Window closed successfully");
1071}
1072
1073// Check if KEY_ESCAPE pressed or Close icon pressed
1075{
1076#if defined(PLATFORM_WEB)
1077 // Emterpreter-Async required to run sync code
1078 // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
1079 // By default, this function is never called on a web-ready raylib example because we encapsulate
1080 // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously
1081 // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter!
1082 emscripten_sleep(16);
1083 return false;
1084#endif
1085
1086#if defined(PLATFORM_DESKTOP)
1087 if (CORE.Window.ready)
1088 {
1089 // While window minimized, stop loop execution
1091
1092 CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle);
1093
1094 // Reset close status for next frame
1096
1097 return CORE.Window.shouldClose;
1098 }
1099 else return true;
1100#endif
1101
1102#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
1103 if (CORE.Window.ready) return CORE.Window.shouldClose;
1104 else return true;
1105#endif
1106}
1107
1108// Check if window has been initialized successfully
1110{
1111 return CORE.Window.ready;
1112}
1113
1114// Check if window is currently fullscreen
1116{
1117 return CORE.Window.fullscreen;
1118}
1119
1120// Check if window is currently hidden
1122{
1123#if defined(PLATFORM_DESKTOP)
1124 return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0);
1125#endif
1126 return false;
1127}
1128
1129// Check if window has been minimized
1131{
1132#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
1133 return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0);
1134#else
1135 return false;
1136#endif
1137}
1138
1139// Check if window has been maximized (only PLATFORM_DESKTOP)
1141{
1142#if defined(PLATFORM_DESKTOP)
1143 return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0);
1144#else
1145 return false;
1146#endif
1147}
1148
1149// Check if window has the focus
1151{
1152#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
1153 return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0);
1154#else
1155 return true;
1156#endif
1157}
1158
1159// Check if window has been resizedLastFrame
1161{
1162#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
1163 return CORE.Window.resizedLastFrame;
1164#else
1165 return false;
1166#endif
1167}
1168
1169// Check if one specific window flag is enabled
1170bool IsWindowState(unsigned int flag)
1171{
1172 return ((CORE.Window.flags & flag) > 0);
1173}
1174
1175// Toggle fullscreen mode (only PLATFORM_DESKTOP)
1177{
1178#if defined(PLATFORM_DESKTOP)
1179 // TODO: glfwSetWindowMonitor() doesn't work properly (bugs)
1180 if (!CORE.Window.fullscreen)
1181 {
1182 // Store previous window position (in case we exit fullscreen)
1183 glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y);
1184
1185 int monitorCount = 0;
1186 int monitorIndex = GetCurrentMonitor();
1187 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1188
1189 // Use current monitor, so we correctly get the display the window is on
1190 GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL;
1191
1192 if (monitor == NULL)
1193 {
1194 TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor");
1195
1196 CORE.Window.fullscreen = false;
1197 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
1198
1200 }
1201 else
1202 {
1203 CORE.Window.fullscreen = true;
1205
1206 glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
1207 }
1208 }
1209 else
1210 {
1211 CORE.Window.fullscreen = false;
1212 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
1213
1215 }
1216
1217 // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
1218 // NOTE: V-Sync can be enabled by graphic driver configuration
1220#endif
1221#if defined(PLATFORM_WEB)
1222/*
1223 EM_ASM
1224 (
1225 // This strategy works well while using raylib minimal web shell for emscripten,
1226 // it re-scales the canvas to fullscreen using monitor resolution, for tools this
1227 // is a good strategy but maybe games prefer to keep current canvas resolution and
1228 // display it in fullscreen, adjusting monitor resolution if possible
1229 if (document.fullscreenElement) document.exitFullscreen();
1230 else Module.requestFullscreen(true, true); //false, true);
1231 );
1232*/
1233 //EM_ASM(Module.requestFullscreen(false, false););
1234/*
1235 if (!CORE.Window.fullscreen)
1236 {
1237 // Option 1: Request fullscreen for the canvas element
1238 // This option does not seem to work at all:
1239 // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security,
1240 // the user must click once on the canvas to hide the pointer or transition to full screen
1241 //emscripten_request_fullscreen("#canvas", false);
1242
1243 // Option 2: Request fullscreen for the canvas element with strategy
1244 // This option does not seem to work at all
1245 // Ref: https://github.com/emscripten-core/emscripten/issues/5124
1246 // EmscriptenFullscreenStrategy strategy = {
1247 // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
1248 // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
1249 // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
1250 // .canvasResizedCallback = EmscriptenWindowResizedCallback,
1251 // .canvasResizedCallbackUserData = NULL
1252 // };
1253 //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy);
1254
1255 // Option 3: Request fullscreen for the canvas element with strategy
1256 // It works as expected but only inside the browser (client area)
1257 EmscriptenFullscreenStrategy strategy = {
1258 .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
1259 .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
1260 .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
1261 .canvasResizedCallback = EmscriptenWindowResizedCallback,
1262 .canvasResizedCallbackUserData = NULL
1263 };
1264 emscripten_enter_soft_fullscreen("#canvas", &strategy);
1265
1266 int width, height;
1267 emscripten_get_canvas_element_size("#canvas", &width, &height);
1268 TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height);
1269
1270 CORE.Window.fullscreen = true; // Toggle fullscreen flag
1271 CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
1272 }
1273 else
1274 {
1275 //emscripten_exit_fullscreen();
1276 //emscripten_exit_soft_fullscreen();
1277
1278 int width, height;
1279 emscripten_get_canvas_element_size("#canvas", &width, &height);
1280 TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height);
1281
1282 CORE.Window.fullscreen = false; // Toggle fullscreen flag
1283 CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
1284 }
1285*/
1286
1287 CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag
1288#endif
1289#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
1290 TRACELOG(LOG_WARNING, "SYSTEM: Failed to toggle to windowed mode");
1291#endif
1292}
1293
1294// Set window state: maximized, if resizable (only PLATFORM_DESKTOP)
1296{
1297#if defined(PLATFORM_DESKTOP)
1299 {
1300 glfwMaximizeWindow(CORE.Window.handle);
1302 }
1303#endif
1304}
1305
1306// Set window state: minimized (only PLATFORM_DESKTOP)
1308{
1309#if defined(PLATFORM_DESKTOP)
1310 // NOTE: Following function launches callback that sets appropiate flag!
1311 glfwIconifyWindow(CORE.Window.handle);
1312#endif
1313}
1314
1315// Set window state: not minimized/maximized (only PLATFORM_DESKTOP)
1317{
1318#if defined(PLATFORM_DESKTOP)
1320 {
1321 // Restores the specified window if it was previously iconified (minimized) or maximized
1322 glfwRestoreWindow(CORE.Window.handle);
1323 CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
1324 CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
1325 }
1326#endif
1327}
1328
1329// Set window configuration state using flags
1330void SetWindowState(unsigned int flags)
1331{
1332#if defined(PLATFORM_DESKTOP)
1333 // Check previous state and requested state to apply required changes
1334 // NOTE: In most cases the functions already change the flags internally
1335
1336 // State change: FLAG_VSYNC_HINT
1337 if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0))
1338 {
1341 }
1342
1343 // State change: FLAG_FULLSCREEN_MODE
1344 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE))
1345 {
1346 ToggleFullscreen(); // NOTE: Window state flag updated inside function
1347 }
1348
1349 // State change: FLAG_WINDOW_RESIZABLE
1350 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
1351 {
1354 }
1355
1356 // State change: FLAG_WINDOW_UNDECORATED
1358 {
1361 }
1362
1363 // State change: FLAG_WINDOW_HIDDEN
1364 if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
1365 {
1366 glfwHideWindow(CORE.Window.handle);
1368 }
1369
1370 // State change: FLAG_WINDOW_MINIMIZED
1371 if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
1372 {
1373 //GLFW_ICONIFIED
1374 MinimizeWindow(); // NOTE: Window state flag updated inside function
1375 }
1376
1377 // State change: FLAG_WINDOW_MAXIMIZED
1378 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
1379 {
1380 //GLFW_MAXIMIZED
1381 MaximizeWindow(); // NOTE: Window state flag updated inside function
1382 }
1383
1384 // State change: FLAG_WINDOW_UNFOCUSED
1385 if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
1386 {
1389 }
1390
1391 // State change: FLAG_WINDOW_TOPMOST
1392 if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
1393 {
1396 }
1397
1398 // State change: FLAG_WINDOW_ALWAYS_RUN
1399 if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
1400 {
1402 }
1403
1404 // The following states can not be changed after window creation
1405
1406 // State change: FLAG_WINDOW_TRANSPARENT
1407 if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
1408 {
1409 TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
1410 }
1411
1412 // State change: FLAG_WINDOW_HIGHDPI
1413 if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
1414 {
1415 TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
1416 }
1417
1418 // State change: FLAG_MSAA_4X_HINT
1419 if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
1420 {
1421 TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
1422 }
1423
1424 // State change: FLAG_INTERLACED_HINT
1425 if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
1426 {
1427 TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
1428 }
1429#endif
1430}
1431
1432// Clear window configuration state flags
1433void ClearWindowState(unsigned int flags)
1434{
1435#if defined(PLATFORM_DESKTOP)
1436 // Check previous state and requested state to apply required changes
1437 // NOTE: In most cases the functions already change the flags internally
1438
1439 // State change: FLAG_VSYNC_HINT
1440 if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0))
1441 {
1443 CORE.Window.flags &= ~FLAG_VSYNC_HINT;
1444 }
1445
1446 // State change: FLAG_FULLSCREEN_MODE
1447 if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0))
1448 {
1449 ToggleFullscreen(); // NOTE: Window state flag updated inside function
1450 }
1451
1452 // State change: FLAG_WINDOW_RESIZABLE
1453 if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
1454 {
1456 CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
1457 }
1458
1459 // State change: FLAG_WINDOW_UNDECORATED
1460 if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0))
1461 {
1463 CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
1464 }
1465
1466 // State change: FLAG_WINDOW_HIDDEN
1467 if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
1468 {
1469 glfwShowWindow(CORE.Window.handle);
1470 CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
1471 }
1472
1473 // State change: FLAG_WINDOW_MINIMIZED
1474 if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
1475 {
1476 RestoreWindow(); // NOTE: Window state flag updated inside function
1477 }
1478
1479 // State change: FLAG_WINDOW_MAXIMIZED
1480 if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
1481 {
1482 RestoreWindow(); // NOTE: Window state flag updated inside function
1483 }
1484
1485 // State change: FLAG_WINDOW_UNFOCUSED
1486 if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
1487 {
1489 CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
1490 }
1491
1492 // State change: FLAG_WINDOW_TOPMOST
1493 if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
1494 {
1496 CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
1497 }
1498
1499 // State change: FLAG_WINDOW_ALWAYS_RUN
1500 if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
1501 {
1502 CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
1503 }
1504
1505 // The following states can not be changed after window creation
1506
1507 // State change: FLAG_WINDOW_TRANSPARENT
1508 if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
1509 {
1510 TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only by configured before window initialization");
1511 }
1512
1513 // State change: FLAG_WINDOW_HIGHDPI
1514 if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
1515 {
1516 TRACELOG(LOG_WARNING, "WINDOW: High DPI can only by configured before window initialization");
1517 }
1518
1519 // State change: FLAG_MSAA_4X_HINT
1520 if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
1521 {
1522 TRACELOG(LOG_WARNING, "WINDOW: MSAA can only by configured before window initialization");
1523 }
1524
1525 // State change: FLAG_INTERLACED_HINT
1526 if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
1527 {
1528 TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only by configured before window initialization");
1529 }
1530#endif
1531}
1532
1533// Set icon for window (only PLATFORM_DESKTOP)
1534// NOTE: Image must be in RGBA format, 8bit per channel
1536{
1537#if defined(PLATFORM_DESKTOP)
1539 {
1540 GLFWimage icon[1] = { 0 };
1541
1542 icon[0].width = image.width;
1543 icon[0].height = image.height;
1544 icon[0].pixels = (unsigned char *)image.data;
1545
1546 // NOTE 1: We only support one image icon
1547 // NOTE 2: The specified image data is copied before this function returns
1548 glfwSetWindowIcon(CORE.Window.handle, 1, icon);
1549 }
1550 else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format");
1551#endif
1552}
1553
1554// Set title for window (only PLATFORM_DESKTOP)
1555void SetWindowTitle(const char *title)
1556{
1557 CORE.Window.title = title;
1558#if defined(PLATFORM_DESKTOP)
1559 glfwSetWindowTitle(CORE.Window.handle, title);
1560#endif
1561}
1562
1563// Set window position on screen (windowed mode)
1564void SetWindowPosition(int x, int y)
1565{
1566#if defined(PLATFORM_DESKTOP)
1567 glfwSetWindowPos(CORE.Window.handle, x, y);
1568#endif
1569}
1570
1571// Set monitor for the current window (fullscreen mode)
1572void SetWindowMonitor(int monitor)
1573{
1574#if defined(PLATFORM_DESKTOP)
1575 int monitorCount = 0;
1576 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1577
1578 if ((monitor >= 0) && (monitor < monitorCount))
1579 {
1580 TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
1581
1582 const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
1583 glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate);
1584 }
1585 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
1586#endif
1587}
1588
1589// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
1590void SetWindowMinSize(int width, int height)
1591{
1592#if defined(PLATFORM_DESKTOP)
1594 glfwSetWindowSizeLimits(CORE.Window.handle, width, height, mode->width, mode->height);
1595#endif
1596}
1597
1598// Set window dimensions
1599void SetWindowSize(int width, int height)
1600{
1601#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
1602 glfwSetWindowSize(CORE.Window.handle, width, height);
1603#endif
1604}
1605
1606// Set window opacity, value opacity is between 0.0 and 1.0
1607void SetWindowOpacity(float opacity)
1608{
1609#if defined(PLATFORM_DESKTOP)
1610 if (opacity >= 1.0f) opacity = 1.0f;
1611 else if (opacity <= 0.0f) opacity = 0.0f;
1612 glfwSetWindowOpacity(CORE.Window.handle, opacity);
1613#endif
1614}
1615
1616// Get current screen width
1618{
1619 return CORE.Window.screen.width;
1620}
1621
1622// Get current screen height
1624{
1625 return CORE.Window.screen.height;
1626}
1627
1628// Get current render width which is equal to screen width * dpi scale
1630{
1631 return CORE.Window.render.width;
1632}
1633
1634// Get current screen height which is equal to screen height * dpi scale
1636{
1637 return CORE.Window.render.height;
1638}
1639
1640// Get native window handle
1642{
1643#if defined(PLATFORM_DESKTOP) && defined(_WIN32)
1644 // NOTE: Returned handle is: void *HWND (windows.h)
1645 return glfwGetWin32Window(CORE.Window.handle);
1646#endif
1647#if defined(__linux__)
1648 // NOTE: Returned handle is: unsigned long Window (X.h)
1649 // typedef unsigned long XID;
1650 // typedef XID Window;
1651 //unsigned long id = (unsigned long)glfwGetX11Window(window);
1652 return NULL; // TODO: Find a way to return value... cast to void *?
1653#endif
1654#if defined(__APPLE__)
1655 // NOTE: Returned handle is: (objc_object *)
1656 return NULL; // TODO: return (void *)glfwGetCocoaWindow(window);
1657#endif
1658
1659 return NULL;
1660}
1661
1662// Get number of monitors
1664{
1665#if defined(PLATFORM_DESKTOP)
1666 int monitorCount;
1667 glfwGetMonitors(&monitorCount);
1668 return monitorCount;
1669#else
1670 return 1;
1671#endif
1672}
1673
1674// Get number of monitors
1676{
1677#if defined(PLATFORM_DESKTOP)
1678 int monitorCount;
1679 GLFWmonitor** monitors = glfwGetMonitors(&monitorCount);
1680 GLFWmonitor* monitor = NULL;
1681
1682 if (monitorCount == 1) // easy out
1683 return 0;
1684
1685 if (IsWindowFullscreen())
1686 {
1687 monitor = glfwGetWindowMonitor(CORE.Window.handle);
1688 for (int i = 0; i < monitorCount; i++)
1689 {
1690 if (monitors[i] == monitor)
1691 return i;
1692 }
1693 return 0;
1694 }
1695 else
1696 {
1697 int x = 0;
1698 int y = 0;
1699
1700 glfwGetWindowPos(CORE.Window.handle, &x, &y);
1701
1702 for (int i = 0; i < monitorCount; i++)
1703 {
1704 int mx = 0;
1705 int my = 0;
1706
1707 int width = 0;
1708 int height = 0;
1709
1710 monitor = monitors[i];
1711 glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height);
1712 if (x >= mx && x <= (mx + width) && y >= my && y <= (my + height))
1713 return i;
1714 }
1715 }
1716 return 0;
1717#else
1718 return 0;
1719#endif
1720}
1721
1722// Get selected monitor width
1724{
1725#if defined(PLATFORM_DESKTOP)
1726 int monitorCount;
1727 GLFWmonitor** monitors = glfwGetMonitors(&monitorCount);
1728
1729 if ((monitor >= 0) && (monitor < monitorCount))
1730 {
1731 int x, y;
1732 glfwGetMonitorPos(monitors[monitor], &x, &y);
1733
1734 return (Vector2){ (float)x, (float)y };
1735 }
1736 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
1737#endif
1738 return (Vector2){ 0, 0 };
1739}
1740
1741// Get selected monitor width (max available by monitor)
1742int GetMonitorWidth(int monitor)
1743{
1744#if defined(PLATFORM_DESKTOP)
1745 int monitorCount;
1746 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1747
1748 if ((monitor >= 0) && (monitor < monitorCount))
1749 {
1750 int count = 0;
1751 const GLFWvidmode *modes = glfwGetVideoModes(monitors[monitor], &count);
1752
1753 // We return the maximum resolution available, the last one in the modes array
1754 if (count > 0) return modes[count - 1].width;
1755 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
1756 }
1757 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
1758#endif
1759 return 0;
1760}
1761
1762// Get selected monitor width (max available by monitor)
1763int GetMonitorHeight(int monitor)
1764{
1765#if defined(PLATFORM_DESKTOP)
1766 int monitorCount;
1767 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1768
1769 if ((monitor >= 0) && (monitor < monitorCount))
1770 {
1771 int count = 0;
1772 const GLFWvidmode *modes = glfwGetVideoModes(monitors[monitor], &count);
1773
1774 // We return the maximum resolution available, the last one in the modes array
1775 if (count > 0) return modes[count - 1].height;
1776 else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
1777 }
1778 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
1779#endif
1780 return 0;
1781}
1782
1783// Get selected monitor physical width in millimetres
1785{
1786#if defined(PLATFORM_DESKTOP)
1787 int monitorCount;
1788 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1789
1790 if ((monitor >= 0) && (monitor < monitorCount))
1791 {
1792 int physicalWidth;
1793 glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL);
1794 return physicalWidth;
1795 }
1796 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
1797#endif
1798 return 0;
1799}
1800
1801// Get primary monitor physical height in millimetres
1803{
1804#if defined(PLATFORM_DESKTOP)
1805 int monitorCount;
1806 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1807
1808 if ((monitor >= 0) && (monitor < monitorCount))
1809 {
1810 int physicalHeight;
1811 glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight);
1812 return physicalHeight;
1813 }
1814 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
1815#endif
1816 return 0;
1817}
1818
1820{
1821#if defined(PLATFORM_DESKTOP)
1822 int monitorCount;
1823 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1824
1825 if ((monitor >= 0) && (monitor < monitorCount))
1826 {
1827 const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]);
1828 return vidmode->refreshRate;
1829 }
1830 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
1831#endif
1832#if defined(PLATFORM_DRM)
1833 if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0))
1834 {
1835 return CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh;
1836 }
1837#endif
1838 return 0;
1839}
1840
1841// Get window position XY on monitor
1843{
1844 int x = 0;
1845 int y = 0;
1846#if defined(PLATFORM_DESKTOP)
1847 glfwGetWindowPos(CORE.Window.handle, &x, &y);
1848#endif
1849 return (Vector2){ (float)x, (float)y };
1850}
1851
1852// Get window scale DPI factor
1854{
1855 Vector2 scale = { 1.0f, 1.0f };
1856
1857#if defined(PLATFORM_DESKTOP)
1858 float xdpi = 1.0;
1859 float ydpi = 1.0;
1860 Vector2 windowPos = GetWindowPosition();
1861
1862 int monitorCount = 0;
1863 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1864
1865 // Check window monitor
1866 for (int i = 0; i < monitorCount; i++)
1867 {
1868 glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi);
1869
1870 int xpos, ypos, width, height;
1871 glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height);
1872
1873 if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) &&
1874 (windowPos.y >= ypos) && (windowPos.y < ypos + height))
1875 {
1876 scale.x = xdpi;
1877 scale.y = ydpi;
1878 break;
1879 }
1880 }
1881#endif
1882
1883 return scale;
1884}
1885
1886// Get the human-readable, UTF-8 encoded name of the primary monitor
1887const char *GetMonitorName(int monitor)
1888{
1889#if defined(PLATFORM_DESKTOP)
1890 int monitorCount;
1891 GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
1892
1893 if ((monitor >= 0) && (monitor < monitorCount))
1894 {
1895 return glfwGetMonitorName(monitors[monitor]);
1896 }
1897 else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
1898#endif
1899 return "";
1900}
1901
1902// Get clipboard text content
1903// NOTE: returned string is allocated and freed by GLFW
1904const char *GetClipboardText(void)
1905{
1906#if defined(PLATFORM_DESKTOP)
1907 return glfwGetClipboardString(CORE.Window.handle);
1908#endif
1909#if defined(PLATFORM_WEB)
1910 return emscripten_run_script_string("navigator.clipboard.readText()");
1911#endif
1912 return NULL;
1913}
1914
1915// Set clipboard text content
1916void SetClipboardText(const char *text)
1917{
1918#if defined(PLATFORM_DESKTOP)
1919 glfwSetClipboardString(CORE.Window.handle, text);
1920#endif
1921#if defined(PLATFORM_WEB)
1922 emscripten_run_script(TextFormat("navigator.clipboard.writeText('%s')", text));
1923#endif
1924}
1925
1926// Show mouse cursor
1927void ShowCursor(void)
1928{
1929#if defined(PLATFORM_DESKTOP)
1931#endif
1932
1933 CORE.Input.Mouse.cursorHidden = false;
1934}
1935
1936// Hides mouse cursor
1937void HideCursor(void)
1938{
1939#if defined(PLATFORM_DESKTOP)
1941#endif
1942
1943 CORE.Input.Mouse.cursorHidden = true;
1944}
1945
1946// Check if cursor is not visible
1948{
1949 return CORE.Input.Mouse.cursorHidden;
1950}
1951
1952// Enables cursor (unlock cursor)
1954{
1955#if defined(PLATFORM_DESKTOP)
1957#endif
1958#if defined(PLATFORM_WEB)
1959 emscripten_exit_pointerlock();
1960#endif
1961
1962 CORE.Input.Mouse.cursorHidden = false;
1963}
1964
1965// Disables cursor (lock cursor)
1967{
1968#if defined(PLATFORM_DESKTOP)
1970#endif
1971#if defined(PLATFORM_WEB)
1972 emscripten_request_pointerlock("#canvas", 1);
1973#endif
1974
1975 CORE.Input.Mouse.cursorHidden = true;
1976}
1977
1978// Check if cursor is on the current screen.
1980{
1981 return CORE.Input.Mouse.cursorOnScreen;
1982}
1983
1984// Set background color (framebuffer clear color)
1986{
1987 rlClearColor(color.r, color.g, color.b, color.a); // Set clear color
1988 rlClearScreenBuffers(); // Clear current framebuffers
1989}
1990
1991// Setup canvas (framebuffer) to start drawing
1993{
1994 // WARNING: Previously to BeginDrawing() other render textures drawing could happen,
1995 // consequently the measure for update vs draw is not accurate (only the total frame time is accurate)
1996
1997 CORE.Time.current = GetTime(); // Number of elapsed seconds since InitTimer()
1998 CORE.Time.update = CORE.Time.current - CORE.Time.previous;
1999 CORE.Time.previous = CORE.Time.current;
2000
2001 rlLoadIdentity(); // Reset current matrix (modelview)
2002 rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling
2003
2004 //rlTranslatef(0.375, 0.375, 0); // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
2005 // NOTE: Not required with OpenGL 3.3+
2006}
2007
2008// End canvas drawing and swap buffers (double buffering)
2009void EndDrawing(void)
2010{
2011 rlDrawRenderBatchActive(); // Update and draw internal render batch
2012
2013#if defined(SUPPORT_MODULE_RSHAPES) && defined(SUPPORT_MOUSE_CURSOR_POINT)
2014 // Draw a small rectangle on mouse position for user reference
2015 if (!CORE.Input.Mouse.cursorHidden)
2016 {
2017 DrawRectangle(CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y, 3, 3, MAROON); // WARNING: Module required: rshapes
2018 rlDrawRenderBatchActive(); // Update and draw internal render batch
2019 }
2020#endif
2021
2022#if defined(SUPPORT_GIF_RECORDING)
2023 // Draw record indicator
2024 if (gifRecording)
2025 {
2026 #define GIF_RECORD_FRAMERATE 10
2027 gifFrameCounter++;
2028
2029 // NOTE: We record one gif frame every 10 game frames
2030 if ((gifFrameCounter%GIF_RECORD_FRAMERATE) == 0)
2031 {
2032 // Get image data for the current frame (from backbuffer)
2033 // NOTE: This process is quite slow... :(
2034 unsigned char *screenData = rlReadScreenPixels(CORE.Window.screen.width, CORE.Window.screen.height);
2035 msf_gif_frame(&gifState, screenData, 10, 16, CORE.Window.screen.width*4);
2036
2037 RL_FREE(screenData); // Free image data
2038 }
2039
2040 #if defined(SUPPORT_MODULE_RSHAPES) && defined(SUPPORT_MODULE_RTEXT)
2041 if (((gifFrameCounter/15)%2) == 1)
2042 {
2043 DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON); // WARNING: Module required: rshapes
2044 DrawText("GIF RECORDING", 50, CORE.Window.screen.height - 25, 10, RED); // WARNING: Module required: rtext
2045 }
2046 #endif
2047
2048 rlDrawRenderBatchActive(); // Update and draw internal render batch
2049 }
2050#endif
2051
2052#if defined(SUPPORT_EVENTS_AUTOMATION)
2053 // Draw record/play indicator
2054 if (eventsRecording)
2055 {
2056 gifFrameCounter++;
2057
2058 if (((gifFrameCounter/15)%2) == 1)
2059 {
2060 DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON);
2061 DrawText("EVENTS RECORDING", 50, CORE.Window.screen.height - 25, 10, RED);
2062 }
2063
2064 rlDrawRenderBatchActive(); // Update and draw internal render batch
2065 }
2066 else if (eventsPlaying)
2067 {
2068 gifFrameCounter++;
2069
2070 if (((gifFrameCounter/15)%2) == 1)
2071 {
2072 DrawCircle(30, CORE.Window.screen.height - 20, 10, LIME);
2073 DrawText("EVENTS PLAYING", 50, CORE.Window.screen.height - 25, 10, GREEN);
2074 }
2075
2076 rlDrawRenderBatchActive(); // Update and draw internal render batch
2077 }
2078#endif
2079
2080#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
2081 SwapScreenBuffer(); // Copy back buffer to front buffer (screen)
2082
2083 // Frame time control system
2084 CORE.Time.current = GetTime();
2085 CORE.Time.draw = CORE.Time.current - CORE.Time.previous;
2086 CORE.Time.previous = CORE.Time.current;
2087
2088 CORE.Time.frame = CORE.Time.update + CORE.Time.draw;
2089
2090 // Wait for some milliseconds...
2091 if (CORE.Time.frame < CORE.Time.target)
2092 {
2093 WaitTime((float)(CORE.Time.target - CORE.Time.frame)*1000.0f);
2094
2095 CORE.Time.current = GetTime();
2096 double waitTime = CORE.Time.current - CORE.Time.previous;
2097 CORE.Time.previous = CORE.Time.current;
2098
2099 CORE.Time.frame += waitTime; // Total frame time: update + draw + wait
2100 }
2101
2102 PollInputEvents(); // Poll user events (before next frame update)
2103#endif
2104
2105#if defined(SUPPORT_EVENTS_AUTOMATION)
2106 // Events recording and playing logic
2107 if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter);
2108 else if (eventsPlaying)
2109 {
2110 // TODO: When should we play? After/before/replace PollInputEvents()?
2111 if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false;
2112 PlayAutomationEvent(CORE.Time.frameCounter);
2113 }
2114#endif
2115
2116 CORE.Time.frameCounter++;
2117}
2118
2119// Initialize 2D mode with custom camera (2D)
2121{
2122 rlDrawRenderBatchActive(); // Update and draw internal render batch
2123
2124 rlLoadIdentity(); // Reset current matrix (modelview)
2125
2126 // Apply 2d camera transformation to modelview
2128
2129 // Apply screen scaling if required
2131}
2132
2133// Ends 2D mode with custom camera
2134void EndMode2D(void)
2135{
2136 rlDrawRenderBatchActive(); // Update and draw internal render batch
2137
2138 rlLoadIdentity(); // Reset current matrix (modelview)
2139 rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
2140}
2141
2142// Initializes 3D mode with custom camera (3D)
2144{
2145 rlDrawRenderBatchActive(); // Update and draw internal render batch
2146
2147 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
2148 rlPushMatrix(); // Save previous matrix, which contains the settings for the 2d ortho projection
2149 rlLoadIdentity(); // Reset current matrix (projection)
2150
2151 float aspect = (float)CORE.Window.currentFbo.width/(float)CORE.Window.currentFbo.height;
2152
2153 // NOTE: zNear and zFar values are important when computing depth buffer values
2154 if (camera.projection == CAMERA_PERSPECTIVE)
2155 {
2156 // Setup perspective projection
2157 double top = RL_CULL_DISTANCE_NEAR*tan(camera.fovy*0.5*DEG2RAD);
2158 double right = top*aspect;
2159
2160 rlFrustum(-right, right, -top, top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
2161 }
2162 else if (camera.projection == CAMERA_ORTHOGRAPHIC)
2163 {
2164 // Setup orthographic projection
2165 double top = camera.fovy/2.0;
2166 double right = top*aspect;
2167
2168 rlOrtho(-right, right, -top,top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
2169 }
2170
2171 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
2172 rlLoadIdentity(); // Reset current matrix (modelview)
2173
2174 // Setup Camera view
2175 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
2176 rlMultMatrixf(MatrixToFloat(matView)); // Multiply modelview matrix by view matrix (camera)
2177
2178 rlEnableDepthTest(); // Enable DEPTH_TEST for 3D
2179}
2180
2181// Ends 3D mode and returns to default 2D orthographic mode
2182void EndMode3D(void)
2183{
2184 rlDrawRenderBatchActive(); // Update and draw internal render batch
2185
2186 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
2187 rlPopMatrix(); // Restore previous matrix (projection) from matrix stack
2188
2189 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
2190 rlLoadIdentity(); // Reset current matrix (modelview)
2191
2192 rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
2193
2194 rlDisableDepthTest(); // Disable DEPTH_TEST for 2D
2195}
2196
2197// Initializes render texture for drawing
2199{
2200 rlDrawRenderBatchActive(); // Update and draw internal render batch
2201
2202 rlEnableFramebuffer(target.id); // Enable render target
2203
2204 // Set viewport and RLGL internal framebuffer size
2205 rlViewport(0, 0, target.texture.width, target.texture.height);
2208
2209 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
2210 rlLoadIdentity(); // Reset current matrix (projection)
2211
2212 // Set orthographic projection to current framebuffer size
2213 // NOTE: Configured top-left corner as (0, 0)
2214 rlOrtho(0, target.texture.width, target.texture.height, 0, 0.0f, 1.0f);
2215
2216 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
2217 rlLoadIdentity(); // Reset current matrix (modelview)
2218
2219 //rlScalef(0.0f, -1.0f, 0.0f); // Flip Y-drawing (?)
2220
2221 // Setup current width/height for proper aspect ratio
2222 // calculation when using BeginMode3D()
2223 CORE.Window.currentFbo.width = target.texture.width;
2224 CORE.Window.currentFbo.height = target.texture.height;
2225}
2226
2227// Ends drawing to render texture
2229{
2230 rlDrawRenderBatchActive(); // Update and draw internal render batch
2231
2232 rlDisableFramebuffer(); // Disable render target (fbo)
2233
2234 // Set viewport to default framebuffer size
2235 SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
2236
2237 // Reset current fbo to screen size
2240}
2241
2242// Begin custom shader mode
2244{
2245 rlSetShader(shader.id, shader.locs);
2246}
2247
2248// End custom shader mode (returns to default shader)
2250{
2252}
2253
2254// Begin blending mode (alpha, additive, multiplied, subtract, custom)
2255// NOTE: Blend modes supported are enumerated in BlendMode enum
2256void BeginBlendMode(int mode)
2257{
2258 rlSetBlendMode(mode);
2259}
2260
2261// End blending mode (reset to default: alpha blending)
2263{
2265}
2266
2267// Begin scissor mode (define screen area for following drawing)
2268// NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left
2269void BeginScissorMode(int x, int y, int width, int height)
2270{
2271 rlDrawRenderBatchActive(); // Update and draw internal render batch
2272
2274
2275#if defined(__APPLE__)
2276 Vector2 scale = GetWindowScaleDPI();
2277 rlScissor((int)(x*scale.x), (int)(GetScreenHeight()*scale.y - (((y + height)*scale.y))), (int)(width*scale.x), (int)(height*scale.y));
2278#else
2279 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
2280 {
2281 Vector2 scale = GetWindowScaleDPI();
2282 rlScissor((int)(x*scale.x), (int)(CORE.Window.currentFbo.height - (y + height)*scale.y), (int)(width*scale.x), (int)(height*scale.y));
2283 }
2284 else
2285 {
2286 rlScissor(x, CORE.Window.currentFbo.height - (y + height), width, height);
2287 }
2288#endif
2289}
2290
2291// End scissor mode
2293{
2294 rlDrawRenderBatchActive(); // Update and draw internal render batch
2296}
2297
2298// Begin VR drawing configuration
2300{
2302
2303 // Set stereo render matrices
2306}
2307
2308// End VR drawing process (and desktop mirror)
2310{
2312}
2313
2314// Load VR stereo config for VR simulator device parameters
2316{
2317 VrStereoConfig config = { 0 };
2318
2319 if ((rlGetVersion() == OPENGL_33) || (rlGetVersion() == OPENGL_ES_20))
2320 {
2321 // Compute aspect ratio
2322 float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution;
2323
2324 // Compute lens parameters
2325 float lensShift = (device.hScreenSize*0.25f - device.lensSeparationDistance*0.5f)/device.hScreenSize;
2326 config.leftLensCenter[0] = 0.25f + lensShift;
2327 config.leftLensCenter[1] = 0.5f;
2328 config.rightLensCenter[0] = 0.75f - lensShift;
2329 config.rightLensCenter[1] = 0.5f;
2330 config.leftScreenCenter[0] = 0.25f;
2331 config.leftScreenCenter[1] = 0.5f;
2332 config.rightScreenCenter[0] = 0.75f;
2333 config.rightScreenCenter[1] = 0.5f;
2334
2335 // Compute distortion scale parameters
2336 // NOTE: To get lens max radius, lensShift must be normalized to [-1..1]
2337 float lensRadius = fabsf(-1.0f - 4.0f*lensShift);
2338 float lensRadiusSq = lensRadius*lensRadius;
2339 float distortionScale = device.lensDistortionValues[0] +
2340 device.lensDistortionValues[1]*lensRadiusSq +
2341 device.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq +
2342 device.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq;
2343
2344 float normScreenWidth = 0.5f;
2345 float normScreenHeight = 1.0f;
2346 config.scaleIn[0] = 2.0f/normScreenWidth;
2347 config.scaleIn[1] = 2.0f/normScreenHeight/aspect;
2348 config.scale[0] = normScreenWidth*0.5f/distortionScale;
2349 config.scale[1] = normScreenHeight*0.5f*aspect/distortionScale;
2350
2351 // Fovy is normally computed with: 2*atan2f(device.vScreenSize, 2*device.eyeToScreenDistance)
2352 // ...but with lens distortion it is increased (see Oculus SDK Documentation)
2353 float fovy = 2.0f*atan2f(device.vScreenSize*0.5f*distortionScale, device.eyeToScreenDistance); // Really need distortionScale?
2354 // float fovy = 2.0f*(float)atan2f(device.vScreenSize*0.5f, device.eyeToScreenDistance);
2355
2356 // Compute camera projection matrices
2357 float projOffset = 4.0f*lensShift; // Scaled to projection space coordinates [-1..1]
2359
2360 config.projection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f));
2361 config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
2362
2363 // Compute camera transformation matrices
2364 // NOTE: Camera movement might seem more natural if we model the head.
2365 // Our axis of rotation is the base of our head, so we might want to add
2366 // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions.
2367 config.viewOffset[0] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
2368 config.viewOffset[1] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
2369
2370 // Compute eyes Viewports
2371 /*
2372 config.eyeViewportRight[0] = 0;
2373 config.eyeViewportRight[1] = 0;
2374 config.eyeViewportRight[2] = device.hResolution/2;
2375 config.eyeViewportRight[3] = device.vResolution;
2376
2377 config.eyeViewportLeft[0] = device.hResolution/2;
2378 config.eyeViewportLeft[1] = 0;
2379 config.eyeViewportLeft[2] = device.hResolution/2;
2380 config.eyeViewportLeft[3] = device.vResolution;
2381 */
2382 }
2383 else TRACELOG(LOG_WARNING, "RLGL: VR Simulator not supported on OpenGL 1.1");
2384
2385 return config;
2386}
2387
2388// Unload VR stereo config properties
2390{
2391 //...
2392}
2393
2394// Load shader from files and bind default locations
2395// NOTE: If shader string is NULL, using default vertex/fragment shaders
2396Shader LoadShader(const char *vsFileName, const char *fsFileName)
2397{
2398 Shader shader = { 0 };
2399
2400 char *vShaderStr = NULL;
2401 char *fShaderStr = NULL;
2402
2403 if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName);
2404 if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName);
2405
2406 shader = LoadShaderFromMemory(vShaderStr, fShaderStr);
2407
2408 UnloadFileText(vShaderStr);
2409 UnloadFileText(fShaderStr);
2410
2411 return shader;
2412}
2413
2414// Load shader from code strings and bind default locations
2415RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
2416{
2417 Shader shader = { 0 };
2418 shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
2419
2420 // NOTE: All locations must be reseted to -1 (no location)
2421 for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
2422
2423 shader.id = rlLoadShaderCode(vsCode, fsCode);
2424
2425 // After shader loading, we TRY to set default location names
2426 if (shader.id > 0)
2427 {
2428 // Default shader attribute locations have been binded before linking:
2429 // vertex position location = 0
2430 // vertex texcoord location = 1
2431 // vertex normal location = 2
2432 // vertex color location = 3
2433 // vertex tangent location = 4
2434 // vertex texcoord2 location = 5
2435
2436 // NOTE: If any location is not found, loc point becomes -1
2437
2438 // Get handles to GLSL input attibute locations
2445
2446 // Get handles to GLSL uniform locations (vertex shader)
2452
2453 // Get handles to GLSL uniform locations (fragment shader)
2458 }
2459
2460 return shader;
2461}
2462
2463// Unload shader from GPU memory (VRAM)
2465{
2466 if (shader.id != rlGetShaderIdDefault())
2467 {
2468 rlUnloadShaderProgram(shader.id);
2469 RL_FREE(shader.locs);
2470 }
2471}
2472
2473// Get shader uniform location
2474int GetShaderLocation(Shader shader, const char *uniformName)
2475{
2476 return rlGetLocationUniform(shader.id, uniformName);
2477}
2478
2479// Get shader attribute location
2480int GetShaderLocationAttrib(Shader shader, const char *attribName)
2481{
2482 return rlGetLocationAttrib(shader.id, attribName);
2483}
2484
2485// Set shader uniform value
2486void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType)
2487{
2488 SetShaderValueV(shader, locIndex, value, uniformType, 1);
2489}
2490
2491// Set shader uniform value vector
2492void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
2493{
2494 rlEnableShader(shader.id);
2495 rlSetUniform(locIndex, value, uniformType, count);
2496 //rlDisableShader(); // Avoid reseting current shader program, in case other uniforms are set
2497}
2498
2499// Set shader uniform value (matrix 4x4)
2500void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
2501{
2502 rlEnableShader(shader.id);
2503 rlSetUniformMatrix(locIndex, mat);
2504 //rlDisableShader();
2505}
2506
2507// Set shader uniform value for texture
2508void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
2509{
2510 rlEnableShader(shader.id);
2511 rlSetUniformSampler(locIndex, texture.id);
2512 //rlDisableShader();
2513}
2514
2515// Get a ray trace from mouse position
2517{
2518 Ray ray = { 0 };
2519
2520 // Calculate normalized device coordinates
2521 // NOTE: y value is negative
2522 float x = (2.0f*mouse.x)/(float)GetScreenWidth() - 1.0f;
2523 float y = 1.0f - (2.0f*mouse.y)/(float)GetScreenHeight();
2524 float z = 1.0f;
2525
2526 // Store values in a vector
2527 Vector3 deviceCoords = { x, y, z };
2528
2529 // Calculate view matrix from camera look at
2530 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
2531
2532 Matrix matProj = MatrixIdentity();
2533
2534 if (camera.projection == CAMERA_PERSPECTIVE)
2535 {
2536 // Calculate projection matrix from perspective
2538 }
2539 else if (camera.projection == CAMERA_ORTHOGRAPHIC)
2540 {
2541 float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
2542 double top = camera.fovy/2.0;
2543 double right = top*aspect;
2544
2545 // Calculate projection matrix from orthographic
2546 matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
2547 }
2548
2549 // Unproject far/near points
2550 Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
2551 Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
2552
2553 // Unproject the mouse cursor in the near plane.
2554 // We need this as the source position because orthographic projects, compared to perspect doesn't have a
2555 // convergence point, meaning that the "eye" of the camera is more like a plane than a point.
2556 Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
2557
2558 // Calculate normalized direction vector
2559 Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));
2560
2561 if (camera.projection == CAMERA_PERSPECTIVE) ray.position = camera.position;
2562 else if (camera.projection == CAMERA_ORTHOGRAPHIC) ray.position = cameraPlanePointerPos;
2563
2564 // Apply calculated vectors to ray
2565 ray.direction = direction;
2566
2567 return ray;
2568}
2569
2570// Get transform matrix for camera
2572{
2573 return MatrixLookAt(camera.position, camera.target, camera.up);
2574}
2575
2576// Get camera 2d transform matrix
2578{
2579 Matrix matTransform = { 0 };
2580 // The camera in world-space is set by
2581 // 1. Move it to target
2582 // 2. Rotate by -rotation and scale by (1/zoom)
2583 // When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller),
2584 // not for the camera getting bigger, hence the invert. Same deal with rotation.
2585 // 3. Move it by (-offset);
2586 // Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera)
2587 // we need to do it into opposite direction (inverse transform)
2588
2589 // Having camera transform in world-space, inverse of it gives the modelview transform.
2590 // Since (A*B*C)' = C'*B'*A', the modelview is
2591 // 1. Move to offset
2592 // 2. Rotate and Scale
2593 // 3. Move by -target
2594 Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
2595 Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
2596 Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
2597 Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f);
2598
2599 matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
2600
2601 return matTransform;
2602}
2603
2604// Get the screen space position from a 3d world space position
2606{
2607 Vector2 screenPosition = GetWorldToScreenEx(position, camera, GetScreenWidth(), GetScreenHeight());
2608
2609 return screenPosition;
2610}
2611
2612// Get size position for a 3d world space position (useful for texture drawing)
2613Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height)
2614{
2615 // Calculate projection matrix (from perspective instead of frustum
2616 Matrix matProj = MatrixIdentity();
2617
2618 if (camera.projection == CAMERA_PERSPECTIVE)
2619 {
2620 // Calculate projection matrix from perspective
2621 matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
2622 }
2623 else if (camera.projection == CAMERA_ORTHOGRAPHIC)
2624 {
2625 float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
2626 double top = camera.fovy/2.0;
2627 double right = top*aspect;
2628
2629 // Calculate projection matrix from orthographic
2630 matProj = MatrixOrtho(-right, right, -top, top, RL_CULL_DISTANCE_NEAR, RL_CULL_DISTANCE_FAR);
2631 }
2632
2633 // Calculate view matrix from camera look at (and transpose it)
2634 Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
2635
2636 // TODO: Why not use Vector3Transform(Vector3 v, Matrix mat)?
2637
2638 // Convert world position vector to quaternion
2639 Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
2640
2641 // Transform world position to view
2642 worldPos = QuaternionTransform(worldPos, matView);
2643
2644 // Transform result to projection (clip space position)
2645 worldPos = QuaternionTransform(worldPos, matProj);
2646
2647 // Calculate normalized device coordinates (inverted y)
2648 Vector3 ndcPos = { worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w };
2649
2650 // Calculate 2d screen position vector
2651 Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)width, (ndcPos.y + 1.0f)/2.0f*(float)height };
2652
2653 return screenPosition;
2654}
2655
2656// Get the screen space position for a 2d camera world space position
2658{
2659 Matrix matCamera = GetCameraMatrix2D(camera);
2660 Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera);
2661
2662 return (Vector2){ transform.x, transform.y };
2663}
2664
2665// Get the world space position for a 2d camera screen space position
2667{
2668 Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera));
2669 Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera);
2670
2671 return (Vector2){ transform.x, transform.y };
2672}
2673
2674// Set target FPS (maximum)
2675void SetTargetFPS(int fps)
2676{
2677 if (fps < 1) CORE.Time.target = 0.0;
2678 else CORE.Time.target = 1.0/(double)fps;
2679
2680 TRACELOG(LOG_INFO, "TIMER: Target time per frame: %02.03f milliseconds", (float)CORE.Time.target*1000.0f);
2681}
2682
2683// Get current FPS
2684// NOTE: We calculate an average framerate
2685int GetFPS(void)
2686{
2687 int fps = 0;
2688
2689#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
2690 #define FPS_CAPTURE_FRAMES_COUNT 30 // 30 captures
2691 #define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 millisecondes
2692 #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT)
2693
2694 static int index = 0;
2695 static float history[FPS_CAPTURE_FRAMES_COUNT] = { 0 };
2696 static float average = 0, last = 0;
2697 float fpsFrame = GetFrameTime();
2698
2699 if (fpsFrame == 0) return 0;
2700
2701 if ((GetTime() - last) > FPS_STEP)
2702 {
2703 last = (float)GetTime();
2704 index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT;
2705 average -= history[index];
2706 history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT;
2707 average += history[index];
2708 }
2709
2710 fps = (int)roundf(1.0f/average);
2711#endif
2712
2713 return fps;
2714}
2715
2716// Get time in seconds for last frame drawn (delta time)
2717float GetFrameTime(void)
2718{
2719 return (float)CORE.Time.frame;
2720}
2721
2722// Get elapsed time measure in seconds since InitTimer()
2723// NOTE: On PLATFORM_DESKTOP InitTimer() is called on InitWindow()
2724// NOTE: On PLATFORM_DESKTOP, timer is initialized on glfwInit()
2725double GetTime(void)
2726{
2727#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
2728 return glfwGetTime(); // Elapsed time since glfwInit()
2729#endif
2730
2731#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
2732 struct timespec ts = { 0 };
2733 clock_gettime(CLOCK_MONOTONIC, &ts);
2734 unsigned long long int time = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
2735
2736 return (double)(time - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
2737#endif
2738}
2739
2740// Setup window configuration flags (view FLAGS)
2741// NOTE: This function is expected to be called before window creation,
2742// because it setups some flags for the window creation process.
2743// To configure window states after creation, just use SetWindowState()
2744void SetConfigFlags(unsigned int flags)
2745{
2746 // Selected flags are set but not evaluated at this point,
2747 // flag evaluation happens at InitWindow() or SetWindowState()
2748 CORE.Window.flags |= flags;
2749}
2750
2751// NOTE TRACELOG() function is located in [utils.h]
2752
2753// Takes a screenshot of current screen (saved a .png)
2754void TakeScreenshot(const char *fileName)
2755{
2756#if defined(SUPPORT_MODULE_RTEXTURES)
2757 unsigned char *imgData = rlReadScreenPixels(CORE.Window.render.width, CORE.Window.render.height);
2758 Image image = { imgData, CORE.Window.render.width, CORE.Window.render.height, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
2759
2760 char path[2048] = { 0 };
2761 strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName));
2762
2763 ExportImage(image, path); // WARNING: Module required: rtextures
2764 RL_FREE(imgData);
2765
2766#if defined(PLATFORM_WEB)
2767 // Download file from MEMFS (emscripten memory filesystem)
2768 // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html
2769 emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path)));
2770#endif
2771
2772 TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path);
2773#else
2774 TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures");
2775#endif
2776}
2777
2778// Get a random value between min and max (both included)
2779int GetRandomValue(int min, int max)
2780{
2781 if (min > max)
2782 {
2783 int tmp = max;
2784 max = min;
2785 min = tmp;
2786 }
2787
2788 return (rand()%(abs(max - min) + 1) + min);
2789}
2790
2791// Set the seed for the random number generator
2792void SetRandomSeed(unsigned int seed)
2793{
2794 srand(seed);
2795}
2796
2797// Check if the file exists
2798bool FileExists(const char *fileName)
2799{
2800 bool result = false;
2801
2802#if defined(_WIN32)
2803 if (_access(fileName, 0) != -1) result = true;
2804#else
2805 if (access(fileName, F_OK) != -1) result = true;
2806#endif
2807
2808 // NOTE: Alternatively, stat() can be used instead of access()
2809 //#include <sys/stat.h>
2810 //struct stat statbuf;
2811 //if (stat(filename, &statbuf) == 0) result = true;
2812
2813 return result;
2814}
2815
2816// Check file extension
2817// NOTE: Extensions checking is not case-sensitive
2818bool IsFileExtension(const char *fileName, const char *ext)
2819{
2820 bool result = false;
2821 const char *fileExt = GetFileExtension(fileName);
2822
2823 if (fileExt != NULL)
2824 {
2825#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_TEXT_MANIPULATION)
2826 int extCount = 0;
2827 const char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext
2828
2829 char fileExtLower[16] = { 0 };
2830 strcpy(fileExtLower, TextToLower(fileExt)); // WARNING: Module required: rtext
2831
2832 for (int i = 0; i < extCount; i++)
2833 {
2834 if (strcmp(fileExtLower, TextToLower(checkExts[i])) == 0)
2835 {
2836 result = true;
2837 break;
2838 }
2839 }
2840#else
2841 if (strcmp(fileExt, ext) == 0) result = true;
2842#endif
2843 }
2844
2845 return result;
2846}
2847
2848// Check if a directory path exists
2849bool DirectoryExists(const char *dirPath)
2850{
2851 bool result = false;
2852 DIR *dir = opendir(dirPath);
2853
2854 if (dir != NULL)
2855 {
2856 result = true;
2857 closedir(dir);
2858 }
2859
2860 return result;
2861}
2862
2863// Get file length in bytes
2864// NOTE: GetFileSize() conflicts with windows.h
2865int GetFileLength(const char *fileName)
2866{
2867 int size = 0;
2868
2869 FILE *file = fopen(fileName, "rb");
2870
2871 if (file != NULL)
2872 {
2873 fseek(file, 0L, SEEK_END);
2874 size = (int)ftell(file);
2875 fclose(file);
2876 }
2877
2878 return size;
2879}
2880
2881// Get pointer to extension for a filename string (includes the dot: .png)
2882const char *GetFileExtension(const char *fileName)
2883{
2884 const char *dot = strrchr(fileName, '.');
2885
2886 if (!dot || dot == fileName) return NULL;
2887
2888 return dot;
2889}
2890
2891// String pointer reverse break: returns right-most occurrence of charset in s
2892static const char *strprbrk(const char *s, const char *charset)
2893{
2894 const char *latestMatch = NULL;
2895 for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
2896 return latestMatch;
2897}
2898
2899// Get pointer to filename for a path string
2900const char *GetFileName(const char *filePath)
2901{
2902 const char *fileName = NULL;
2903 if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
2904
2905 if (!fileName) return filePath;
2906
2907 return fileName + 1;
2908}
2909
2910// Get filename string without extension (uses static string)
2911const char *GetFileNameWithoutExt(const char *filePath)
2912{
2913 #define MAX_FILENAMEWITHOUTEXT_LENGTH 128
2914
2915 static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
2916 memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
2917
2918 if (filePath != NULL) strcpy(fileName, GetFileName(filePath)); // Get filename with extension
2919
2920 int size = (int)strlen(fileName); // Get size in bytes
2921
2922 for (int i = 0; (i < size) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
2923 {
2924 if (fileName[i] == '.')
2925 {
2926 // NOTE: We break on first '.' found
2927 fileName[i] = '\0';
2928 break;
2929 }
2930 }
2931
2932 return fileName;
2933}
2934
2935// Get directory for a given filePath
2936const char *GetDirectoryPath(const char *filePath)
2937{
2938/*
2939 // NOTE: Directory separator is different in Windows and other platforms,
2940 // fortunately, Windows also support the '/' separator, that's the one should be used
2941 #if defined(_WIN32)
2942 char separator = '\\';
2943 #else
2944 char separator = '/';
2945 #endif
2946*/
2947 const char *lastSlash = NULL;
2948 static char dirPath[MAX_FILEPATH_LENGTH] = { 0 };
2949 memset(dirPath, 0, MAX_FILEPATH_LENGTH);
2950
2951 // In case provided path does not contain a root drive letter (C:\, D:\‍) nor leading path separator (\, /),
2952 // we add the current directory path to dirPath
2953 if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/')
2954 {
2955 // For security, we set starting path to current directory,
2956 // obtained path will be concated to this
2957 dirPath[0] = '.';
2958 dirPath[1] = '/';
2959 }
2960
2961 lastSlash = strprbrk(filePath, "\\/");
2962 if (lastSlash)
2963 {
2964 if (lastSlash == filePath)
2965 {
2966 // The last and only slash is the leading one: path is in a root directory
2967 dirPath[0] = filePath[0];
2968 dirPath[1] = '\0';
2969 }
2970 else
2971 {
2972 // NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
2973 memcpy(dirPath + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0), filePath, strlen(filePath) - (strlen(lastSlash) - 1));
2974 dirPath[strlen(filePath) - strlen(lastSlash) + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0)] = '\0'; // Add '\0' manually
2975 }
2976 }
2977
2978 return dirPath;
2979}
2980
2981// Get previous directory path for a given path
2982const char *GetPrevDirectoryPath(const char *dirPath)
2983{
2984 static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
2985 memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
2986 int pathLen = (int)strlen(dirPath);
2987
2988 if (pathLen <= 3) strcpy(prevDirPath, dirPath);
2989
2990 for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--)
2991 {
2992 if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
2993 {
2994 // Check for root: "C:\" or "/"
2995 if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
2996
2997 strncpy(prevDirPath, dirPath, i);
2998 break;
2999 }
3000 }
3001
3002 return prevDirPath;
3003}
3004
3005// Get current working directory
3006const char *GetWorkingDirectory(void)
3007{
3008 static char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
3009 memset(currentDir, 0, MAX_FILEPATH_LENGTH);
3010
3011 char *path = GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
3012
3013 return path;
3014}
3015
3017{
3018 static char appDir[MAX_FILEPATH_LENGTH] = { 0 };
3019 memset(appDir, 0, MAX_FILEPATH_LENGTH);
3020
3021#if defined(_WIN32)
3022 int len = 0;
3023#if defined (UNICODE)
3024 unsigned short widePath[MAX_PATH];
3025 len = GetModuleFileNameW(NULL, widePath, MAX_PATH);
3026 len = WideCharToMultiByte(0, 0, widePath, len, appDir, MAX_PATH, NULL, NULL);
3027#else
3028 len = GetModuleFileNameA(NULL, appDir, MAX_PATH);
3029#endif
3030 if (len > 0)
3031 {
3032 for (int i = len; i >= 0; --i)
3033 {
3034 if (appDir[i] == '\\')
3035 {
3036 appDir[i + 1] = '\0';
3037 break;
3038 }
3039 }
3040 }
3041 else
3042 {
3043 appDir[0] = '.';
3044 appDir[1] = '\\';
3045 }
3046
3047#elif defined(__linux__)
3048 unsigned int size = sizeof(appDir);
3049 ssize_t len = readlink("/proc/self/exe", appDir, size);
3050
3051 if (len > 0)
3052 {
3053 for (int i = len; i >= 0; --i)
3054 {
3055 if (appDir[i] == '/')
3056 {
3057 appDir[i + 1] = '\0';
3058 break;
3059 }
3060 }
3061 }
3062 else
3063 {
3064 appDir[0] = '.';
3065 appDir[1] = '/';
3066 }
3067#elif defined(__APPLE__)
3068 uint32_t size = sizeof(appDir);
3069
3070 if (_NSGetExecutablePath(appDir, &size) == 0)
3071 {
3072 int len = strlen(appDir);
3073 for (int i = len; i >= 0; --i)
3074 {
3075 if (appDir[i] == '/')
3076 {
3077 appDir[i + 1] = '\0';
3078 break;
3079 }
3080 }
3081 }
3082 else
3083 {
3084 appDir[0] = '.';
3085 appDir[1] = '/';
3086 }
3087#endif
3088
3089 return appDir;
3090}
3091
3092// Get filenames in a directory path
3093// NOTE: Files count is returned by parameters pointer
3094char **GetDirectoryFiles(const char *dirPath, int *fileCount)
3095{
3097
3098 int counter = 0;
3099 struct dirent *entity;
3100 DIR *dir = opendir(dirPath);
3101
3102 if (dir != NULL) // It's a directory
3103 {
3104 // Count files
3105 while ((entity = readdir(dir)) != NULL) counter++;
3106
3107 dirFileCount = counter;
3108 *fileCount = dirFileCount;
3109
3110 // Memory allocation for dirFileCount
3111 dirFilesPath = (char **)RL_MALLOC(dirFileCount*sizeof(char *));
3112 for (int i = 0; i < dirFileCount; i++) dirFilesPath[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char));
3113
3114 // Reset our position in the directory to the beginning
3115 rewinddir(dir);
3116
3117 // Read file names
3118 for (int i = 0; (entity = readdir(dir)) != NULL; i++) strcpy(dirFilesPath[i], entity->d_name);
3119
3120 closedir(dir);
3121 }
3122 else TRACELOG(LOG_WARNING, "FILEIO: Failed to open requested directory"); // Maybe it's a file...
3123
3124 return dirFilesPath;
3125}
3126
3127// Clear directory files paths buffers
3129{
3130 if (dirFileCount > 0)
3131 {
3132 for (int i = 0; i < dirFileCount; i++) RL_FREE(dirFilesPath[i]);
3133
3134 RL_FREE(dirFilesPath);
3135 }
3136
3137 dirFileCount = 0;
3138}
3139
3140// Change working directory, returns true on success
3141bool ChangeDirectory(const char *dir)
3142{
3143 bool result = CHDIR(dir);
3144
3145 if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir);
3146
3147 return (result == 0);
3148}
3149
3150// Check if a file has been dropped into window
3152{
3153 if (CORE.Window.dropFileCount > 0) return true;
3154 else return false;
3155}
3156
3157// Get dropped files names
3158char **GetDroppedFiles(int *count)
3159{
3160 *count = CORE.Window.dropFileCount;
3161 return CORE.Window.dropFilesPath;
3162}
3163
3164// Clear dropped files paths buffer
3166{
3167 if (CORE.Window.dropFileCount > 0)
3168 {
3169 for (int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilesPath[i]);
3170
3172
3173 CORE.Window.dropFileCount = 0;
3174 }
3175}
3176
3177// Get file modification time (last write time)
3178long GetFileModTime(const char *fileName)
3179{
3180 struct stat result = { 0 };
3181
3182 if (stat(fileName, &result) == 0)
3183 {
3184 time_t mod = result.st_mtime;
3185
3186 return (long)mod;
3187 }
3188
3189 return 0;
3190}
3191
3192// Compress data (DEFLATE algorythm)
3193unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize)
3194{
3195 #define COMPRESSION_QUALITY_DEFLATE 8
3196
3197 unsigned char *compData = NULL;
3198
3199#if defined(SUPPORT_COMPRESSION_API)
3200 // Compress data and generate a valid DEFLATE stream
3201 struct sdefl sdefl = { 0 };
3202 int bounds = sdefl_bound(dataSize);
3203 compData = (unsigned char *)RL_CALLOC(bounds, 1);
3204 *compDataSize = sdeflate(&sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbwi
3205
3206 TraceLog(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize);
3207#endif
3208
3209 return compData;
3210}
3211
3212// Decompress data (DEFLATE algorythm)
3213unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize)
3214{
3215 unsigned char *data = NULL;
3216
3217#if defined(SUPPORT_COMPRESSION_API)
3218 // Decompress data from a valid DEFLATE stream
3219 data = RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
3220 int length = sinflate(data, MAX_DECOMPRESSION_SIZE, compData, compDataSize);
3221 unsigned char *temp = RL_REALLOC(data, length);
3222
3223 if (temp != NULL) data = temp;
3224 else TRACELOG(LOG_WARNING, "SYSTEM: Failed to re-allocate required decompression memory");
3225
3226 *dataSize = length;
3227
3228 TraceLog(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize);
3229#endif
3230
3231 return data;
3232}
3233
3234// Encode data to Base64 string
3235char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
3236{
3237 static const unsigned char base64encodeTable[] = {
3238 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
3239 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
3240 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
3241 };
3242
3243 static const int modTable[] = { 0, 2, 1 };
3244
3245 *outputSize = 4*((dataSize + 2)/3);
3246
3247 char *encodedData = RL_MALLOC(*outputSize);
3248
3249 if (encodedData == NULL) return NULL;
3250
3251 for (int i = 0, j = 0; i < dataSize;)
3252 {
3253 unsigned int octetA = (i < dataSize)? (unsigned char)data[i++] : 0;
3254 unsigned int octetB = (i < dataSize)? (unsigned char)data[i++] : 0;
3255 unsigned int octetC = (i < dataSize)? (unsigned char)data[i++] : 0;
3256
3257 unsigned int triple = (octetA << 0x10) + (octetB << 0x08) + octetC;
3258
3259 encodedData[j++] = base64encodeTable[(triple >> 3*6) & 0x3F];
3260 encodedData[j++] = base64encodeTable[(triple >> 2*6) & 0x3F];
3261 encodedData[j++] = base64encodeTable[(triple >> 1*6) & 0x3F];
3262 encodedData[j++] = base64encodeTable[(triple >> 0*6) & 0x3F];
3263 }
3264
3265 for (int i = 0; i < modTable[dataSize%3]; i++) encodedData[*outputSize - 1 - i] = '='; // Padding character
3266
3267 return encodedData;
3268}
3269
3270// Decode Base64 string data
3271unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize)
3272{
3273 static const unsigned char base64decodeTable[] = {
3274 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
3275 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
3276 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
3277 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
3278 };
3279
3280 // Get output size of Base64 input data
3281 int outSize = 0;
3282 for (int i = 0; data[4*i] != 0; i++)
3283 {
3284 if (data[4*i + 3] == '=')
3285 {
3286 if (data[4*i + 2] == '=') outSize += 1;
3287 else outSize += 2;
3288 }
3289 else outSize += 3;
3290 }
3291
3292 // Allocate memory to store decoded Base64 data
3293 unsigned char *decodedData = (unsigned char *)RL_MALLOC(outSize);
3294
3295 for (int i = 0; i < outSize/3; i++)
3296 {
3297 unsigned char a = base64decodeTable[(int)data[4*i]];
3298 unsigned char b = base64decodeTable[(int)data[4*i + 1]];
3299 unsigned char c = base64decodeTable[(int)data[4*i + 2]];
3300 unsigned char d = base64decodeTable[(int)data[4*i + 3]];
3301
3302 decodedData[3*i] = (a << 2) | (b >> 4);
3303 decodedData[3*i + 1] = (b << 4) | (c >> 2);
3304 decodedData[3*i + 2] = (c << 6) | d;
3305 }
3306
3307 if (outSize%3 == 1)
3308 {
3309 int n = outSize/3;
3310 unsigned char a = base64decodeTable[(int)data[4*n]];
3311 unsigned char b = base64decodeTable[(int)data[4*n + 1]];
3312 decodedData[outSize - 1] = (a << 2) | (b >> 4);
3313 }
3314 else if (outSize%3 == 2)
3315 {
3316 int n = outSize/3;
3317 unsigned char a = base64decodeTable[(int)data[4*n]];
3318 unsigned char b = base64decodeTable[(int)data[4*n + 1]];
3319 unsigned char c = base64decodeTable[(int)data[4*n + 2]];
3320 decodedData[outSize - 2] = (a << 2) | (b >> 4);
3321 decodedData[outSize - 1] = (b << 4) | (c >> 2);
3322 }
3323
3324 *outputSize = outSize;
3325 return decodedData;
3326}
3327
3328// Save integer value to storage file (to defined position)
3329// NOTE: Storage positions is directly related to file memory layout (4 bytes each integer)
3330bool SaveStorageValue(unsigned int position, int value)
3331{
3332 bool success = false;
3333
3334#if defined(SUPPORT_DATA_STORAGE)
3335 char path[512] = { 0 };
3336 strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, STORAGE_DATA_FILE));
3337
3338 unsigned int dataSize = 0;
3339 unsigned int newDataSize = 0;
3340 unsigned char *fileData = LoadFileData(path, &dataSize);
3341 unsigned char *newFileData = NULL;
3342
3343 if (fileData != NULL)
3344 {
3345 if (dataSize <= (position*sizeof(int)))
3346 {
3347 // Increase data size up to position and store value
3348 newDataSize = (position + 1)*sizeof(int);
3349 newFileData = (unsigned char *)RL_REALLOC(fileData, newDataSize);
3350
3351 if (newFileData != NULL)
3352 {
3353 // RL_REALLOC succeded
3354 int *dataPtr = (int *)newFileData;
3355 dataPtr[position] = value;
3356 }
3357 else
3358 {
3359 // RL_REALLOC failed
3360 TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to realloc data (%u), position in bytes (%u) bigger than actual file size", path, dataSize, position*sizeof(int));
3361
3362 // We store the old size of the file
3363 newFileData = fileData;
3364 newDataSize = dataSize;
3365 }
3366 }
3367 else
3368 {
3369 // Store the old size of the file
3370 newFileData = fileData;
3371 newDataSize = dataSize;
3372
3373 // Replace value on selected position
3374 int *dataPtr = (int *)newFileData;
3375 dataPtr[position] = value;
3376 }
3377
3378 success = SaveFileData(path, newFileData, newDataSize);
3379 RL_FREE(newFileData);
3380
3381 TRACELOG(LOG_INFO, "FILEIO: [%s] Saved storage value: %i", path, value);
3382 }
3383 else
3384 {
3385 TRACELOG(LOG_INFO, "FILEIO: [%s] File created successfully", path);
3386
3387 dataSize = (position + 1)*sizeof(int);
3388 fileData = (unsigned char *)RL_MALLOC(dataSize);
3389 int *dataPtr = (int *)fileData;
3390 dataPtr[position] = value;
3391
3392 success = SaveFileData(path, fileData, dataSize);
3393 UnloadFileData(fileData);
3394
3395 TRACELOG(LOG_INFO, "FILEIO: [%s] Saved storage value: %i", path, value);
3396 }
3397#endif
3398
3399 return success;
3400}
3401
3402// Load integer value from storage file (from defined position)
3403// NOTE: If requested position could not be found, value 0 is returned
3404int LoadStorageValue(unsigned int position)
3405{
3406 int value = 0;
3407
3408#if defined(SUPPORT_DATA_STORAGE)
3409 char path[512] = { 0 };
3410 strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, STORAGE_DATA_FILE));
3411
3412 unsigned int dataSize = 0;
3413 unsigned char *fileData = LoadFileData(path, &dataSize);
3414
3415 if (fileData != NULL)
3416 {
3417 if (dataSize < (position*4)) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to find storage position: %i", path, position);
3418 else
3419 {
3420 int *dataPtr = (int *)fileData;
3421 value = dataPtr[position];
3422 }
3423
3424 UnloadFileData(fileData);
3425
3426 TRACELOG(LOG_INFO, "FILEIO: [%s] Loaded storage value: %i", path, value);
3427 }
3428#endif
3429 return value;
3430}
3431
3432// Open URL with default system browser (if available)
3433// NOTE: This function is only safe to use if you control the URL given.
3434// A user could craft a malicious string performing another action.
3435// Only call this function yourself not with user input or make sure to check the string yourself.
3436// Ref: https://github.com/raysan5/raylib/issues/686
3437void OpenURL(const char *url)
3438{
3439 // Small security check trying to avoid (partially) malicious code...
3440 // sorry for the inconvenience when you hit this point...
3441 if (strchr(url, '\'') != NULL)
3442 {
3443 TRACELOG(LOG_WARNING, "SYSTEM: Provided URL is not valid");
3444 }
3445 else
3446 {
3447#if defined(PLATFORM_DESKTOP)
3448 char *cmd = (char *)RL_CALLOC(strlen(url) + 10, sizeof(char));
3449 #if defined(_WIN32)
3450 sprintf(cmd, "explorer \"%s\"", url);
3451 #endif
3452 #if defined(__linux__) || defined(__FreeBSD__)
3453 sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
3454 #endif
3455 #if defined(__APPLE__)
3456 sprintf(cmd, "open '%s'", url);
3457 #endif
3458 int result = system(cmd);
3459 if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
3460 RL_FREE(cmd);
3461#endif
3462#if defined(PLATFORM_WEB)
3463 emscripten_run_script(TextFormat("window.open('%s', '_blank')", url));
3464#endif
3465#if defined(PLATFORM_ANDROID)
3466 JNIEnv *env = NULL;
3467 JavaVM *vm = CORE.Android.app->activity->vm;
3468 (*vm)->AttachCurrentThread(vm, &env, NULL);
3469
3470 jstring urlString = (*env)->NewStringUTF(env, url);
3471 jclass uriClass = (*env)->FindClass(env, "android/net/Uri");
3472 jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
3473 jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString);
3474
3475 jclass intentClass = (*env)->FindClass(env, "android/content/Intent");
3476 jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;");
3477 jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId);
3478 jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "<init>", "(Ljava/lang/String;Landroid/net/Uri;)V");
3479 jobject intent = (*env)->AllocObject(env, intentClass);
3480
3481 (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri);
3482 jclass activityClass = (*env)->FindClass(env, "android/app/Activity");
3483 jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V");
3484 (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent);
3485
3486 (*vm)->DetachCurrentThread(vm);
3487#endif
3488 }
3489}
3490
3491//----------------------------------------------------------------------------------
3492// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions
3493//----------------------------------------------------------------------------------
3494// Check if a key has been pressed once
3495bool IsKeyPressed(int key)
3496{
3497 bool pressed = false;
3498
3499 if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true;
3500
3501 return pressed;
3502}
3503
3504// Check if a key is being pressed (key held down)
3505bool IsKeyDown(int key)
3506{
3507 if (CORE.Input.Keyboard.currentKeyState[key] == 1) return true;
3508 else return false;
3509}
3510
3511// Check if a key has been released once
3512bool IsKeyReleased(int key)
3513{
3514 bool released = false;
3515
3516 if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true;
3517
3518 return released;
3519}
3520
3521// Check if a key is NOT being pressed (key not held down)
3522bool IsKeyUp(int key)
3523{
3524 if (CORE.Input.Keyboard.currentKeyState[key] == 0) return true;
3525 else return false;
3526}
3527
3528// Get the last key pressed
3530{
3531 int value = 0;
3532
3533 if (CORE.Input.Keyboard.keyPressedQueueCount > 0)
3534 {
3535 // Get character from the queue head
3536 value = CORE.Input.Keyboard.keyPressedQueue[0];
3537
3538 // Shift elements 1 step toward the head.
3539 for (int i = 0; i < (CORE.Input.Keyboard.keyPressedQueueCount - 1); i++)
3541
3542 // Reset last character in the queue
3545 }
3546
3547 return value;
3548}
3549
3550// Get the last char pressed
3552{
3553 int value = 0;
3554
3556 {
3557 // Get character from the queue head
3558 value = CORE.Input.Keyboard.charPressedQueue[0];
3559
3560 // Shift elements 1 step toward the head.
3561 for (int i = 0; i < (CORE.Input.Keyboard.charPressedQueueCount - 1); i++)
3563
3564 // Reset last character in the queue
3567 }
3568
3569 return value;
3570}
3571
3572// Set a custom key to exit program
3573// NOTE: default exitKey is ESCAPE
3574void SetExitKey(int key)
3575{
3576#if !defined(PLATFORM_ANDROID)
3577 CORE.Input.Keyboard.exitKey = key;
3578#endif
3579}
3580
3581// NOTE: Gamepad support not implemented in emscripten GLFW3 (PLATFORM_WEB)
3582
3583// Check if a gamepad is available
3584bool IsGamepadAvailable(int gamepad)
3585{
3586 bool result = false;
3587
3588 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad]) result = true;
3589
3590 return result;
3591}
3592
3593// Get gamepad internal name id
3594const char *GetGamepadName(int gamepad)
3595{
3596#if defined(PLATFORM_DESKTOP)
3597 if (CORE.Input.Gamepad.ready[gamepad]) return glfwGetJoystickName(gamepad);
3598 else return NULL;
3599#endif
3600#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
3601 if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]);
3602 return CORE.Input.Gamepad.name[gamepad];
3603#endif
3604#if defined(PLATFORM_WEB)
3605 return CORE.Input.Gamepad.name[gamepad];
3606#endif
3607 return NULL;
3608}
3609
3610// Get gamepad axis count
3611int GetGamepadAxisCount(int gamepad)
3612{
3613#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
3614 int axisCount = 0;
3615 if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount);
3616 CORE.Input.Gamepad.axisCount = axisCount;
3617#endif
3618
3619 return CORE.Input.Gamepad.axisCount;
3620}
3621
3622// Get axis movement vector for a gamepad
3623float GetGamepadAxisMovement(int gamepad, int axis)
3624{
3625 float value = 0;
3626
3627 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) &&
3628 (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis]; // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA
3629
3630 return value;
3631}
3632
3633// Check if a gamepad button has been pressed once
3634bool IsGamepadButtonPressed(int gamepad, int button)
3635{
3636 bool pressed = false;
3637
3638 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3639 (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 0) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) pressed = true;
3640
3641 return pressed;
3642}
3643
3644// Check if a gamepad button is being pressed
3645bool IsGamepadButtonDown(int gamepad, int button)
3646{
3647 bool result = false;
3648
3649 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3650 (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) result = true;
3651
3652 return result;
3653}
3654
3655// Check if a gamepad button has NOT been pressed once
3656bool IsGamepadButtonReleased(int gamepad, int button)
3657{
3658 bool released = false;
3659
3660 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3661 (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 1) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) released = true;
3662
3663 return released;
3664}
3665
3666// Check if a gamepad button is NOT being pressed
3667bool IsGamepadButtonUp(int gamepad, int button)
3668{
3669 bool result = false;
3670
3671 if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
3672 (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) result = true;
3673
3674 return result;
3675}
3676
3677// Get the last gamepad button pressed
3679{
3680 return CORE.Input.Gamepad.lastButtonPressed;
3681}
3682
3683// Set internal gamepad mappings
3684int SetGamepadMappings(const char *mappings)
3685{
3686 int result = 0;
3687
3688#if defined(PLATFORM_DESKTOP)
3689 result = glfwUpdateGamepadMappings(mappings);
3690#endif
3691
3692 return result;
3693}
3694
3695// Check if a mouse button has been pressed once
3696bool IsMouseButtonPressed(int button)
3697{
3698 bool pressed = false;
3699
3700 if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) pressed = true;
3701
3702 // Map touches to mouse buttons checking
3703 if ((CORE.Input.Touch.currentTouchState[button] == 1) && (CORE.Input.Touch.previousTouchState[button] == 0)) pressed = true;
3704
3705 return pressed;
3706}
3707
3708// Check if a mouse button is being pressed
3709bool IsMouseButtonDown(int button)
3710{
3711 bool down = false;
3712
3713 if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true;
3714
3715 // Map touches to mouse buttons checking
3716 if (CORE.Input.Touch.currentTouchState[button] == 1) down = true;
3717
3718 return down;
3719}
3720
3721// Check if a mouse button has been released once
3723{
3724 bool released = false;
3725
3726 if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) released = true;
3727
3728 // Map touches to mouse buttons checking
3729 if ((CORE.Input.Touch.currentTouchState[button] == 0) && (CORE.Input.Touch.previousTouchState[button] == 1)) released = true;
3730
3731 return released;
3732}
3733
3734// Check if a mouse button is NOT being pressed
3735bool IsMouseButtonUp(int button)
3736{
3737 return !IsMouseButtonDown(button);
3738}
3739
3740// Get mouse position X
3741int GetMouseX(void)
3742{
3743#if defined(PLATFORM_ANDROID)
3744 return (int)CORE.Input.Touch.position[0].x;
3745#else
3746 return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
3747#endif
3748}
3749
3750// Get mouse position Y
3751int GetMouseY(void)
3752{
3753#if defined(PLATFORM_ANDROID)
3754 return (int)CORE.Input.Touch.position[0].y;
3755#else
3756 return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
3757#endif
3758}
3759
3760// Get mouse position XY
3762{
3763 Vector2 position = { 0 };
3764
3765#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
3766 position = GetTouchPosition(0);
3767#else
3768 position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x;
3769 position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y;
3770#endif
3771
3772 return position;
3773}
3774
3775// Get mouse delta between frames
3777{
3778 Vector2 delta = {0};
3779
3782
3783 return delta;
3784}
3785
3786// Set mouse position XY
3787void SetMousePosition(int x, int y)
3788{
3789 CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
3790#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
3791 // NOTE: emscripten not implemented
3793#endif
3794}
3795
3796// Set mouse offset
3797// NOTE: Useful when rendering to different size targets
3798void SetMouseOffset(int offsetX, int offsetY)
3799{
3800 CORE.Input.Mouse.offset = (Vector2){ (float)offsetX, (float)offsetY };
3801}
3802
3803// Set mouse scaling
3804// NOTE: Useful when rendering to different size targets
3805void SetMouseScale(float scaleX, float scaleY)
3806{
3807 CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY };
3808}
3809
3810// Get mouse wheel movement Y
3812{
3813#if defined(PLATFORM_ANDROID)
3814 return 0.0f;
3815#endif
3816#if defined(PLATFORM_WEB)
3817 return CORE.Input.Mouse.currentWheelMove/100.0f;
3818#endif
3819
3820 return CORE.Input.Mouse.currentWheelMove;
3821}
3822
3823// Set mouse cursor
3824// NOTE: This is a no-op on platforms other than PLATFORM_DESKTOP
3825void SetMouseCursor(int cursor)
3826{
3827#if defined(PLATFORM_DESKTOP)
3828 CORE.Input.Mouse.cursor = cursor;
3829 if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL);
3830 else
3831 {
3832 // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values
3833 glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor));
3834 }
3835#endif
3836}
3837
3838// Get touch position X for touch point 0 (relative to screen size)
3839int GetTouchX(void)
3840{
3841#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
3842 return (int)CORE.Input.Touch.position[0].x;
3843#else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM
3844 return GetMouseX();
3845#endif
3846}
3847
3848// Get touch position Y for touch point 0 (relative to screen size)
3849int GetTouchY(void)
3850{
3851#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB)
3852 return (int)CORE.Input.Touch.position[0].y;
3853#else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM
3854 return GetMouseY();
3855#endif
3856}
3857
3858// Get touch position XY for a touch point index (relative to screen size)
3859// TODO: Touch position should be scaled depending on display size and render size
3861{
3862 Vector2 position = { -1.0f, -1.0f };
3863
3864#if defined(PLATFORM_DESKTOP)
3865 // TODO: GLFW does not support multi-touch input just yet
3866 // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
3867 // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
3868 if (index == 0) position = GetMousePosition();
3869#endif
3870#if defined(PLATFORM_ANDROID)
3871 if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
3872 else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
3873
3874 if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
3875 {
3876 position.x = position.x*((float)CORE.Window.screen.width/(float)(CORE.Window.display.width - CORE.Window.renderOffset.x)) - CORE.Window.renderOffset.x/2;
3877 position.y = position.y*((float)CORE.Window.screen.height/(float)(CORE.Window.display.height - CORE.Window.renderOffset.y)) - CORE.Window.renderOffset.y/2;
3878 }
3879 else
3880 {
3881 position.x = position.x*((float)CORE.Window.render.width/(float)CORE.Window.display.width) - CORE.Window.renderOffset.x/2;
3882 position.y = position.y*((float)CORE.Window.render.height/(float)CORE.Window.display.height) - CORE.Window.renderOffset.y/2;
3883 }
3884#endif
3885#if defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
3886 if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
3887 else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
3888#endif
3889
3890 return position;
3891}
3892
3893// Get touch point identifier for given index
3894int GetTouchPointId(int index)
3895{
3896 int id = -1;
3897
3898 if (index < MAX_TOUCH_POINTS) id = CORE.Input.Touch.pointId[index];
3899
3900 return id;
3901}
3902
3903// Get number of touch points
3905{
3906 return CORE.Input.Touch.pointCount;
3907}
3908
3909//----------------------------------------------------------------------------------
3910// Module specific Functions Definition
3911//----------------------------------------------------------------------------------
3912
3913// Initialize display device and framebuffer
3914// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size
3915// If width or height are 0, default display size will be used for framebuffer size
3916// NOTE: returns false in case graphic device could not be created
3917static bool InitGraphicsDevice(int width, int height)
3918{
3919 CORE.Window.screen.width = width; // User desired width
3920 CORE.Window.screen.height = height; // User desired height
3921 CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default
3922
3923 // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars...
3924 // ...in top-down or left-right to match display aspect ratio (no weird scalings)
3925
3926#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
3927 glfwSetErrorCallback(ErrorCallback);
3928/*
3929 // TODO: Setup GLFW custom allocators to match raylib ones
3930 const GLFWallocator allocator = {
3931 .allocate = MemAlloc,
3932 .deallocate = MemFree,
3933 .reallocate = MemRealloc,
3934 .user = NULL
3935 };
3936
3937 glfwInitAllocator(&allocator);
3938*/
3939#if defined(__APPLE__)
3941#endif
3942
3943 if (!glfwInit())
3944 {
3945 TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW");
3946 return false;
3947 }
3948
3949 // NOTE: Getting video modes is not implemented in emscripten GLFW3 version
3950#if defined(PLATFORM_DESKTOP)
3951 // Find monitor resolution
3953 if (!monitor)
3954 {
3955 TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
3956 return false;
3957 }
3958 const GLFWvidmode *mode = glfwGetVideoMode(monitor);
3959
3960 CORE.Window.display.width = mode->width;
3961 CORE.Window.display.height = mode->height;
3962
3963 // Set screen width/height to the display width/height if they are 0
3964 if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
3965 if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
3966#endif // PLATFORM_DESKTOP
3967
3968#if defined(PLATFORM_WEB)
3969 CORE.Window.display.width = CORE.Window.screen.width;
3971#endif // PLATFORM_WEB
3972
3973 glfwDefaultWindowHints(); // Set default windows hints
3974 //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits
3975 //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits
3976 //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits
3977 //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits
3978 //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits
3979 //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window
3980 //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API
3981 //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
3982
3983 // Check window creation flags
3984 if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
3985
3986 if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
3987 else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden
3988
3989 if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
3990 else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window
3991
3992 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
3993 else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable
3994
3995 // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
3996 if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
3997
3998 // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
3999 if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
4000
4003
4006
4007 // NOTE: Some GLFW flags are not supported on HTML5
4008#if defined(PLATFORM_DESKTOP)
4009 if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer
4010 else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer
4011
4012 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
4013 {
4014 // Resize window content area based on the monitor content scale.
4015 // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11.
4016 // On platforms like macOS the resolution of the framebuffer is changed independently of the window size.
4017 glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on
4018 #if defined(__APPLE__)
4020 #endif
4021 }
4023#endif
4024
4025 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
4026 {
4027 // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
4028 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
4029 glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0
4030 }
4031
4032 // NOTE: When asking for an OpenGL context version, most drivers provide highest supported version
4033 // with forward compatibility to older OpenGL versions.
4034 // For example, if using OpenGL 1.1, driver can provide a 4.3 context forward compatible.
4035
4036 // Check selection OpenGL version
4037 if (rlGetVersion() == OPENGL_21)
4038 {
4039 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint)
4040 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint)
4041 }
4042 else if (rlGetVersion() == OPENGL_33)
4043 {
4044 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
4045 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
4046 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
4047 // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
4048#if defined(__APPLE__)
4049 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires fordward compatibility
4050#else
4051 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Fordward Compatibility Hint: Only 3.3 and above!
4052#endif
4053 //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
4054 }
4055 else if (rlGetVersion() == OPENGL_43)
4056 {
4057 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint)
4058 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
4061#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
4062 glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context
4063#endif
4064 }
4065 else if (rlGetVersion() == OPENGL_ES_20) // Request OpenGL ES 2.0 context
4066 {
4070#if defined(PLATFORM_DESKTOP)
4072#else
4074#endif
4075 }
4076
4077#if defined(PLATFORM_DESKTOP)
4078 // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions.
4079 // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn.
4080 // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience.
4081 // REF: https://github.com/raysan5/raylib/issues/1554
4083#endif
4084
4085 if (CORE.Window.fullscreen)
4086 {
4087 // remember center for switchinging from fullscreen to window
4088 CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
4089 CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
4090
4091 if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
4092 if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
4093
4094 // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor
4095 int count = 0;
4096 const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count);
4097
4098 // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height
4099 for (int i = 0; i < count; i++)
4100 {
4101 if ((unsigned int)modes[i].width >= CORE.Window.screen.width)
4102 {
4103 if ((unsigned int)modes[i].height >= CORE.Window.screen.height)
4104 {
4105 CORE.Window.display.width = modes[i].width;
4106 CORE.Window.display.height = modes[i].height;
4107 break;
4108 }
4109 }
4110 }
4111
4112#if defined(PLATFORM_DESKTOP)
4113 // If we are windowed fullscreen, ensures that window does not minimize when focus is lost
4114 if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
4115 {
4117 }
4118#endif
4119 TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
4120
4121 // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
4122 // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
4123 // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
4124 // by the sides to fit all monitor space...
4125
4126 // Try to setup the most appropiate fullscreen framebuffer for the requested screenWidth/screenHeight
4127 // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
4128 // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
4129 // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
4130 // HighDPI monitors are properly considered in a following similar function: SetupViewport()
4131 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
4132
4133 CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
4134
4135 // NOTE: Full-screen change, not working properly...
4136 //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
4137 }
4138 else
4139 {
4140 // No-fullscreen window creation
4141 CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
4142
4143 if (CORE.Window.handle)
4144 {
4145#if defined(PLATFORM_DESKTOP)
4146 // Center window on screen
4147 int windowPosX = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
4148 int windowPosY = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
4149
4150 if (windowPosX < 0) windowPosX = 0;
4151 if (windowPosY < 0) windowPosY = 0;
4152
4153 glfwSetWindowPos(CORE.Window.handle, windowPosX, windowPosY);
4154#endif
4155 CORE.Window.render.width = CORE.Window.screen.width;
4156 CORE.Window.render.height = CORE.Window.screen.height;
4157 }
4158 }
4159
4160 if (!CORE.Window.handle)
4161 {
4162 glfwTerminate();
4163 TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
4164 return false;
4165 }
4166
4167 // Set window callback events
4168 glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default!
4169#if !defined(PLATFORM_WEB)
4170 glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback);
4171#endif
4172 glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback);
4173 glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback);
4174 glfwSetDropCallback(CORE.Window.handle, WindowDropCallback);
4175 // Set input callback events
4176 glfwSetKeyCallback(CORE.Window.handle, KeyCallback);
4177 glfwSetCharCallback(CORE.Window.handle, CharCallback);
4178 glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback);
4179 glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes
4180 glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback);
4181 glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback);
4182
4183 glfwMakeContextCurrent(CORE.Window.handle);
4184
4185#if !defined(PLATFORM_WEB)
4186 glfwSwapInterval(0); // No V-Sync by default
4187#endif
4188
4189 // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
4190 // NOTE: V-Sync can be enabled by graphic driver configuration
4191 if (CORE.Window.flags & FLAG_VSYNC_HINT)
4192 {
4193 // WARNING: It seems to hits a critical render path in Intel HD Graphics
4195 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC");
4196 }
4197
4198 int fbWidth = CORE.Window.screen.width;
4199 int fbHeight = CORE.Window.screen.height;
4200
4201#if defined(PLATFORM_DESKTOP)
4202 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
4203 {
4204 // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling
4205 // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
4206 #if !defined(__APPLE__)
4207 glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight);
4208
4209 // Screen scaling matrix is required in case desired screen area is different than display area
4210 CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
4211
4212 // Mouse input scaling for the new screen size
4213 SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight);
4214 #endif
4215 }
4216#endif
4217
4218 CORE.Window.render.width = fbWidth;
4219 CORE.Window.render.height = fbHeight;
4220 CORE.Window.currentFbo.width = fbWidth;
4221 CORE.Window.currentFbo.height = fbHeight;
4222
4223 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
4224 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
4225 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
4226 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
4227 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
4228
4229#endif // PLATFORM_DESKTOP || PLATFORM_WEB
4230
4231#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
4232 CORE.Window.fullscreen = true;
4234
4235#if defined(PLATFORM_RPI)
4236 bcm_host_init();
4237
4238 DISPMANX_ELEMENT_HANDLE_T dispmanElement = { 0 };
4239 DISPMANX_DISPLAY_HANDLE_T dispmanDisplay = { 0 };
4240 DISPMANX_UPDATE_HANDLE_T dispmanUpdate = { 0 };
4241
4242 VC_RECT_T dstRect = { 0 };
4243 VC_RECT_T srcRect = { 0 };
4244#endif
4245
4246#if defined(PLATFORM_DRM)
4247 CORE.Window.fd = -1;
4248 CORE.Window.connector = NULL;
4249 CORE.Window.modeIndex = -1;
4250 CORE.Window.crtc = NULL;
4251 CORE.Window.gbmDevice = NULL;
4252 CORE.Window.gbmSurface = NULL;
4253 CORE.Window.prevBO = NULL;
4254 CORE.Window.prevFB = 0;
4255
4256#if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
4257 CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
4258#else
4259 TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card");
4260 CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4)
4261
4262 if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL))
4263 {
4264 TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1");
4265 CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded
4266 }
4267
4268 if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL))
4269 {
4270 TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0");
4271 CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
4272 }
4273#endif
4274 if (-1 == CORE.Window.fd)
4275 {
4276 TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card");
4277 return false;
4278 }
4279
4280 drmModeRes *res = drmModeGetResources(CORE.Window.fd);
4281 if (!res)
4282 {
4283 TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources");
4284 return false;
4285 }
4286
4287 TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors);
4288 for (size_t i = 0; i < res->count_connectors; i++)
4289 {
4290 TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i);
4291 drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]);
4292 TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes);
4293 if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id))
4294 {
4295 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected");
4296 CORE.Window.connector = con;
4297 break;
4298 }
4299 else
4300 {
4301 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)");
4302 drmModeFreeConnector(con);
4303 }
4304 }
4305 if (!CORE.Window.connector)
4306 {
4307 TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found");
4308 drmModeFreeResources(res);
4309 return false;
4310 }
4311
4312 drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id);
4313 if (!enc)
4314 {
4315 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder");
4316 drmModeFreeResources(res);
4317 return false;
4318 }
4319
4320 CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id);
4321 if (!CORE.Window.crtc)
4322 {
4323 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc");
4324 drmModeFreeEncoder(enc);
4325 drmModeFreeResources(res);
4326 return false;
4327 }
4328
4329 // If InitWindow should use the current mode find it in the connector's mode list
4330 if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0))
4331 {
4332 TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode...");
4333
4334 CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode);
4335
4336 if (CORE.Window.modeIndex < 0)
4337 {
4338 TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found");
4339 drmModeFreeEncoder(enc);
4340 drmModeFreeResources(res);
4341 return false;
4342 }
4343
4344 CORE.Window.screen.width = CORE.Window.display.width;
4346 }
4347
4348 const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT;
4349 const int fps = (CORE.Time.target > 0) ? (1.0/CORE.Time.target) : 60;
4350 // try to find an exact matching mode
4351 CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
4352 // if nothing found, try to find a nearly matching mode
4353 if (CORE.Window.modeIndex < 0)
4354 CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
4355 // if nothing found, try to find an exactly matching mode including interlaced
4356 if (CORE.Window.modeIndex < 0)
4357 CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
4358 // if nothing found, try to find a nearly matching mode including interlaced
4359 if (CORE.Window.modeIndex < 0)
4360 CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
4361 // if nothing found, there is no suitable mode
4362 if (CORE.Window.modeIndex < 0)
4363 {
4364 TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode");
4365 drmModeFreeEncoder(enc);
4366 drmModeFreeResources(res);
4367 return false;
4368 }
4369
4370 CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay;
4371 CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay;
4372
4373 TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name,
4374 CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay,
4375 (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p',
4376 CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh);
4377
4378 // Use the width and height of the surface for render
4379 CORE.Window.render.width = CORE.Window.screen.width;
4380 CORE.Window.render.height = CORE.Window.screen.height;
4381
4382 drmModeFreeEncoder(enc);
4383 enc = NULL;
4384
4385 drmModeFreeResources(res);
4386 res = NULL;
4387
4388 CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd);
4389 if (!CORE.Window.gbmDevice)
4390 {
4391 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device");
4392 return false;
4393 }
4394
4395 CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay,
4396 CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
4397 if (!CORE.Window.gbmSurface)
4398 {
4399 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface");
4400 return false;
4401 }
4402#endif
4403
4404 EGLint samples = 0;
4405 EGLint sampleBuffer = 0;
4406 if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
4407 {
4408 samples = 4;
4409 sampleBuffer = 1;
4410 TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
4411 }
4412
4413 const EGLint framebufferAttribs[] =
4414 {
4415 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Type of context support -> Required on RPI?
4416#if defined(PLATFORM_DRM)
4417 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android!
4418#endif
4419 EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5)
4420 EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6)
4421 EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5)
4422#if defined(PLATFORM_DRM)
4423 EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer)
4424#endif
4425 //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
4426 EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
4427 //EGL_STENCIL_SIZE, 8, // Stencil buffer size
4428 EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
4429 EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
4430 EGL_NONE
4431 };
4432
4433 const EGLint contextAttribs[] =
4434 {
4436 EGL_NONE
4437 };
4438
4439#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
4440 EGLint numConfigs = 0;
4441
4442 // Get an EGL device connection
4443#if defined(PLATFORM_DRM)
4444 CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice);
4445#else
4447#endif
4448 if (CORE.Window.device == EGL_NO_DISPLAY)
4449 {
4450 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
4451 return false;
4452 }
4453
4454 // Initialize the EGL device connection
4455 if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE)
4456 {
4457 // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
4458 TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
4459 return false;
4460 }
4461
4462#if defined(PLATFORM_DRM)
4463 if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs))
4464 {
4465 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
4466 return false;
4467 }
4468
4469 TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs);
4470
4471 EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs));
4472 if (!configs)
4473 {
4474 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
4475 return false;
4476 }
4477
4478 EGLint matchingNumConfigs = 0;
4479 if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
4480 {
4481 TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
4482 free(configs);
4483 return false;
4484 }
4485
4486 TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs);
4487
4488 // find the EGL config that matches the previously setup GBM format
4489 int found = 0;
4490 for (EGLint i = 0; i < matchingNumConfigs; ++i)
4491 {
4492 EGLint id = 0;
4493 if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
4494 {
4495 TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError());
4496 continue;
4497 }
4498
4499 if (GBM_FORMAT_ARGB8888 == id)
4500 {
4501 TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i);
4502 CORE.Window.config = configs[i];
4503 found = 1;
4504 break;
4505 }
4506 }
4507
4508 RL_FREE(configs);
4509
4510 if (!found)
4511 {
4512 TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config");
4513 return false;
4514 }
4515#else
4516 // Get an appropriate EGL framebuffer configuration
4517 eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs);
4518#endif
4519
4520 // Set rendering API
4522
4523 // Create an EGL rendering context
4524 CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs);
4525 if (CORE.Window.context == EGL_NO_CONTEXT)
4526 {
4527 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
4528 return false;
4529 }
4530#endif
4531
4532 // Create an EGL window surface
4533 //---------------------------------------------------------------------------------
4534#if defined(PLATFORM_ANDROID)
4535 EGLint displayFormat = 0;
4536
4537 // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
4538 // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
4539 eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
4540
4541 // At this point we need to manage render size vs screen size
4542 // NOTE: This function use and modify global module variables:
4543 // -> CORE.Window.screen.width/CORE.Window.screen.height
4544 // -> CORE.Window.render.width/CORE.Window.render.height
4545 // -> CORE.Window.screenScale
4546 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
4547
4548 ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
4549 //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size
4550
4551 CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL);
4552#endif // PLATFORM_ANDROID
4553
4554#if defined(PLATFORM_RPI)
4555 graphics_get_display_size(0, &CORE.Window.display.width, &CORE.Window.display.height);
4556
4557 // Screen size security check
4558 if (CORE.Window.screen.width <= 0) CORE.Window.screen.width = CORE.Window.display.width;
4559 if (CORE.Window.screen.height <= 0) CORE.Window.screen.height = CORE.Window.display.height;
4560
4561 // At this point we need to manage render size vs screen size
4562 // NOTE: This function use and modify global module variables:
4563 // -> CORE.Window.screen.width/CORE.Window.screen.height
4564 // -> CORE.Window.render.width/CORE.Window.render.height
4565 // -> CORE.Window.screenScale
4566 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
4567
4568 dstRect.x = 0;
4569 dstRect.y = 0;
4570 dstRect.width = CORE.Window.display.width;
4571 dstRect.height = CORE.Window.display.height;
4572
4573 srcRect.x = 0;
4574 srcRect.y = 0;
4575 srcRect.width = CORE.Window.render.width << 16;
4576 srcRect.height = CORE.Window.render.height << 16;
4577
4578 // NOTE: RPI dispmanx windowing system takes care of source rectangle scaling to destination rectangle by hardware (no cost)
4579 // Take care that renderWidth/renderHeight fit on displayWidth/displayHeight aspect ratio
4580
4581 VC_DISPMANX_ALPHA_T alpha = { 0 };
4582 alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS;
4583 //alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE; // TODO: Allow transparent framebuffer! -> FLAG_WINDOW_TRANSPARENT
4584 alpha.opacity = 255; // Set transparency level for framebuffer, requires EGLAttrib: EGL_TRANSPARENT_TYPE
4585 alpha.mask = 0;
4586
4587 dispmanDisplay = vc_dispmanx_display_open(0); // LCD
4588 dispmanUpdate = vc_dispmanx_update_start(0);
4589
4590 dispmanElement = vc_dispmanx_element_add(dispmanUpdate, dispmanDisplay, 0/*layer*/, &dstRect, 0/*src*/,
4591 &srcRect, DISPMANX_PROTECTION_NONE, &alpha, 0/*clamp*/, DISPMANX_NO_ROTATE);
4592
4593 CORE.Window.handle.element = dispmanElement;
4594 CORE.Window.handle.width = CORE.Window.render.width;
4595 CORE.Window.handle.height = CORE.Window.render.height;
4596 vc_dispmanx_update_submit_sync(dispmanUpdate);
4597
4598 CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, &CORE.Window.handle, NULL);
4599
4600 const unsigned char *const renderer = glGetString(GL_RENDERER);
4601 if (renderer) TRACELOG(LOG_INFO, "DISPLAY: Renderer name is: %s", renderer);
4602 else TRACELOG(LOG_WARNING, "DISPLAY: Failed to get renderer name");
4603 //---------------------------------------------------------------------------------
4604#endif // PLATFORM_RPI
4605
4606#if defined(PLATFORM_DRM)
4607 CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL);
4608 if (EGL_NO_SURFACE == CORE.Window.surface)
4609 {
4610 TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
4611 return false;
4612 }
4613
4614 // At this point we need to manage render size vs screen size
4615 // NOTE: This function use and modify global module variables:
4616 // -> CORE.Window.screen.width/CORE.Window.screen.height
4617 // -> CORE.Window.render.width/CORE.Window.render.height
4618 // -> CORE.Window.screenScale
4619 SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
4620#endif // PLATFORM_DRM
4621
4622 // There must be at least one frame displayed before the buffers are swapped
4623 //eglSwapInterval(CORE.Window.device, 1);
4624
4625 if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE)
4626 {
4627 TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface");
4628 return false;
4629 }
4630 else
4631 {
4632 CORE.Window.render.width = CORE.Window.screen.width;
4633 CORE.Window.render.height = CORE.Window.screen.height;
4636
4637 TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
4638 TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
4639 TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
4640 TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
4641 TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
4642 }
4643#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM
4644
4645 // Load OpenGL extensions
4646 // NOTE: GL procedures address loader is required to load extensions
4647#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
4649#else
4651#endif
4652
4653 // Initialize OpenGL context (states and resources)
4654 // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
4656
4657 // Setup default viewport
4658 // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
4659 SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
4660
4661 ClearBackground(RAYWHITE); // Default background color for raylib games :P
4662
4663#if defined(PLATFORM_ANDROID)
4664 CORE.Window.ready = true;
4665#endif
4666
4668
4669 return true;
4670}
4671
4672// Set viewport for a provided width and height
4673static void SetupViewport(int width, int height)
4674{
4675 CORE.Window.render.width = width;
4676 CORE.Window.render.height = height;
4677
4678 // Set viewport width and height
4679 // NOTE: We consider render size (scaled) and offset in case black bars are required and
4680 // render area does not match full display area (this situation is only applicable on fullscreen mode)
4681#if defined(__APPLE__)
4682 float xScale = 1.0f, yScale = 1.0f;
4683 glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale);
4684 rlViewport(CORE.Window.renderOffset.x/2*xScale, CORE.Window.renderOffset.y/2*yScale, (CORE.Window.render.width)*xScale, (CORE.Window.render.height)*yScale);
4685#else
4687#endif
4688
4689 rlMatrixMode(RL_PROJECTION); // Switch to projection matrix
4690 rlLoadIdentity(); // Reset current matrix (projection)
4691
4692 // Set orthographic projection to current framebuffer size
4693 // NOTE: Configured top-left corner as (0, 0)
4694 rlOrtho(0, CORE.Window.render.width, CORE.Window.render.height, 0, 0.0f, 1.0f);
4695
4696 rlMatrixMode(RL_MODELVIEW); // Switch back to modelview matrix
4697 rlLoadIdentity(); // Reset current matrix (modelview)
4698}
4699
4700// Compute framebuffer size relative to screen size and display size
4701// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
4702static void SetupFramebuffer(int width, int height)
4703{
4704 // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
4705 if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
4706 {
4707 TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
4708
4709 // Downscaling to fit display with border-bars
4710 float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
4711 float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
4712
4713 if (widthRatio <= heightRatio)
4714 {
4715 CORE.Window.render.width = CORE.Window.display.width;
4716 CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
4717 CORE.Window.renderOffset.x = 0;
4719 }
4720 else
4721 {
4722 CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
4724 CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
4725 CORE.Window.renderOffset.y = 0;
4726 }
4727
4728 // Screen scaling required
4729 float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
4730 CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
4731
4732 // NOTE: We render to full display resolution!
4733 // We just need to calculate above parameters for downscale matrix and offsets
4734 CORE.Window.render.width = CORE.Window.display.width;
4736
4737 TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
4738 }
4739 else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
4740 {
4741 // Required screen size is smaller than display size
4742 TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
4743
4744 if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
4745 {
4746 CORE.Window.screen.width = CORE.Window.display.width;
4748 }
4749
4750 // Upscaling to fit display with border-bars
4751 float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
4752 float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
4753
4754 if (displayRatio <= screenRatio)
4755 {
4756 CORE.Window.render.width = CORE.Window.screen.width;
4757 CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
4758 CORE.Window.renderOffset.x = 0;
4760 }
4761 else
4762 {
4763 CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
4764 CORE.Window.render.height = CORE.Window.screen.height;
4765 CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
4766 CORE.Window.renderOffset.y = 0;
4767 }
4768 }
4769 else
4770 {
4771 CORE.Window.render.width = CORE.Window.screen.width;
4772 CORE.Window.render.height = CORE.Window.screen.height;
4773 CORE.Window.renderOffset.x = 0;
4774 CORE.Window.renderOffset.y = 0;
4775 }
4776}
4777
4778// Initialize hi-resolution timer
4779static void InitTimer(void)
4780{
4781// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
4782// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
4783// High resolutions can also prevent the CPU power management system from entering power-saving modes.
4784// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.
4785#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
4786 timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
4787#endif
4788
4789#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
4790 struct timespec now = { 0 };
4791
4792 if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
4793 {
4794 CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;
4795 }
4796 else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available");
4797#endif
4798
4799 CORE.Time.previous = GetTime(); // Get time as double
4800}
4801
4802// Wait for some milliseconds (stop program execution)
4803// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
4804// take longer than expected... for that reason we use the busy wait loop
4805// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
4806// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timming on Win32!
4807void WaitTime(float ms)
4808{
4809#if defined(SUPPORT_BUSY_WAIT_LOOP)
4810 double previousTime = GetTime();
4811 double currentTime = 0.0;
4812
4813 // Busy wait loop
4814 while ((currentTime - previousTime) < ms/1000.0f) currentTime = GetTime();
4815#else
4816 #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
4817 double busyWait = ms*0.05; // NOTE: We are using a busy wait of 5% of the time
4818 ms -= (float)busyWait;
4819 #endif
4820
4821 // System halt functions
4822 #if defined(_WIN32)
4823 Sleep((unsigned int)ms);
4824 #endif
4825 #if defined(__linux__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__)
4826 struct timespec req = { 0 };
4827 time_t sec = (int)(ms/1000.0f);
4828 ms -= (sec*1000);
4829 req.tv_sec = sec;
4830 req.tv_nsec = ms*1000000L;
4831
4832 // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated.
4833 while (nanosleep(&req, &req) == -1) continue;
4834 #endif
4835 #if defined(__APPLE__)
4836 usleep(ms*1000.0f);
4837 #endif
4838
4839 #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
4840 double previousTime = GetTime();
4841 double currentTime = 0.0;
4842
4843 // Partial busy wait loop (only a fraction of the total wait time)
4844 while ((currentTime - previousTime) < (busyWait/1000.0f)) currentTime = GetTime();
4845 #endif
4846#endif
4847}
4848
4849// Swap back buffer with front buffer (screen drawing)
4851{
4852#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
4853 glfwSwapBuffers(CORE.Window.handle);
4854#endif
4855
4856#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
4857 eglSwapBuffers(CORE.Window.device, CORE.Window.surface);
4858
4859#if defined(PLATFORM_DRM)
4860
4861 if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
4862
4863 struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface);
4864 if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer");
4865
4866 uint32_t fb = 0;
4867 int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb);
4868 if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result);
4869
4870 result = drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0, &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]);
4871 if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result);
4872
4873 if (CORE.Window.prevFB)
4874 {
4875 result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB);
4876 if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result);
4877 }
4878
4879 CORE.Window.prevFB = fb;
4880
4881 if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO);
4882
4883 CORE.Window.prevBO = bo;
4884
4885#endif // PLATFORM_DRM
4886#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM
4887}
4888
4889// Register all input events
4891{
4892#if defined(SUPPORT_GESTURES_SYSTEM)
4893 // NOTE: Gestures update must be called every frame to reset gestures correctly
4894 // because ProcessGestureEvent() is just called on an event, not every frame
4896#endif
4897
4898 // Reset keys/chars pressed registered
4901
4902#if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM))
4903 // Reset last gamepad button/axis registered state
4905 CORE.Input.Gamepad.axisCount = 0;
4906#endif
4907
4908#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
4909 // Register previous keys states
4910 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
4911
4912 PollKeyboardEvents();
4913
4914 // Register previous mouse states
4916 CORE.Input.Mouse.currentWheelMove = 0.0f;
4917 for (int i = 0; i < MAX_MOUSE_BUTTONS; i++)
4918 {
4920 CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i];
4921 }
4922
4923 // Register gamepads buttons events
4924 for (int i = 0; i < MAX_GAMEPADS; i++)
4925 {
4926 if (CORE.Input.Gamepad.ready[i])
4927 {
4928 // Register previous gamepad states
4929 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
4930 }
4931 }
4932#endif
4933
4934#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
4935 // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback)
4936
4937 // Register previous keys states
4938 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
4939
4940 // Register previous mouse states
4941 for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
4942
4943 // Register previous mouse wheel state
4945 CORE.Input.Mouse.currentWheelMove = 0.0f;
4946
4947 // Register previous mouse position
4949#endif
4950
4951 // Register previous touch states
4952 for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
4953
4954 // Reset touch positions
4955 // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event,
4956 // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
4957 //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
4958
4959#if defined(PLATFORM_DESKTOP)
4960 // Check if gamepads are ready
4961 // NOTE: We do it here in case of disconnection
4962 for (int i = 0; i < MAX_GAMEPADS; i++)
4963 {
4964 if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true;
4965 else CORE.Input.Gamepad.ready[i] = false;
4966 }
4967
4968 // Register gamepads buttons events
4969 for (int i = 0; i < MAX_GAMEPADS; i++)
4970 {
4971 if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available
4972 {
4973 // Register previous gamepad states
4974 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
4975
4976 // Get current gamepad state
4977 // NOTE: There is no callback available, so we get it manually
4978 // Get remapped buttons
4979 GLFWgamepadstate state = { 0 };
4980 glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller
4981
4982 const unsigned char *buttons = state.buttons;
4983
4984 for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++)
4985 {
4986 GamepadButton button = -1;
4987
4988 switch (k)
4989 {
4994
4997
5001
5006
5009 default: break;
5010 }
5011
5012 if (button != -1) // Check for valid button
5013 {
5014 if (buttons[k] == GLFW_PRESS)
5015 {
5016 CORE.Input.Gamepad.currentButtonState[i][button] = 1;
5017 CORE.Input.Gamepad.lastButtonPressed = button;
5018 }
5019 else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
5020 }
5021 }
5022
5023 // Get current axis state
5024 const float *axes = state.axes;
5025
5026 for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++)
5027 {
5028 CORE.Input.Gamepad.axisState[i][k] = axes[k];
5029 }
5030
5031 // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis)
5034
5036 }
5037 }
5038
5039 CORE.Window.resizedLastFrame = false;
5040
5041#if defined(SUPPORT_EVENTS_WAITING)
5043#else
5044 glfwPollEvents(); // Register keyboard/mouse events (callbacks)... and window events!
5045#endif
5046#endif // PLATFORM_DESKTOP
5047
5048#if defined(PLATFORM_WEB)
5049 CORE.Window.resizedLastFrame = false;
5050#endif // PLATFORM_WEB
5051
5052// Gamepad support using emscripten API
5053// NOTE: GLFW3 joystick functionality not available in web
5054#if defined(PLATFORM_WEB)
5055 // Get number of gamepads connected
5056 int numGamepads = 0;
5057 if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads();
5058
5059 for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++)
5060 {
5061 // Register previous gamepad button states
5062 for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
5063
5064 EmscriptenGamepadEvent gamepadState;
5065
5066 int result = emscripten_get_gamepad_status(i, &gamepadState);
5067
5068 if (result == EMSCRIPTEN_RESULT_SUCCESS)
5069 {
5070 // Register buttons data for every connected gamepad
5071 for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++)
5072 {
5073 GamepadButton button = -1;
5074
5075 // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface
5076 switch (j)
5077 {
5078 case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
5079 case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
5080 case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
5081 case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
5082 case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
5083 case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
5084 case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
5085 case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
5086 case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
5087 case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
5088 case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
5089 case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
5090 case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
5091 case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
5092 case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
5093 case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
5094 default: break;
5095 }
5096
5097 if (button != -1) // Check for valid button
5098 {
5099 if (gamepadState.digitalButton[j] == 1)
5100 {
5101 CORE.Input.Gamepad.currentButtonState[i][button] = 1;
5102 CORE.Input.Gamepad.lastButtonPressed = button;
5103 }
5104 else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
5105 }
5106
5107 //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
5108 }
5109
5110 // Register axis data for every connected gamepad
5111 for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++)
5112 {
5113 CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j];
5114 }
5115
5116 CORE.Input.Gamepad.axisCount = gamepadState.numAxes;
5117 }
5118 }
5119#endif
5120
5121#if defined(PLATFORM_ANDROID)
5122 // Register previous keys states
5123 // NOTE: Android supports up to 260 keys
5124 for (int i = 0; i < 260; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
5125
5126 // Android ALooper_pollAll() variables
5127 int pollResult = 0;
5128 int pollEvents = 0;
5129
5130 // Poll Events (registered events)
5131 // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled)
5132 while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0)
5133 {
5134 // Process this event
5135 if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source);
5136
5137 // NOTE: Never close window, native activity is controlled by the system!
5138 if (CORE.Android.app->destroyRequested != 0)
5139 {
5140 //CORE.Window.shouldClose = true;
5141 //ANativeActivity_finish(CORE.Android.app->activity);
5142 }
5143 }
5144#endif
5145
5146#if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI)
5147 // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
5148 // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
5149
5150 if (!CORE.Input.Keyboard.evtMode) ProcessKeyboard();
5151
5152 // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread()
5153 // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread()
5154#endif
5155}
5156
5157#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
5158// GLFW3 Error Callback, runs on GLFW3 error
5159static void ErrorCallback(int error, const char *description)
5160{
5161 TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
5162}
5163
5164// GLFW3 WindowSize Callback, runs when window is resizedLastFrame
5165// NOTE: Window resizing not allowed by default
5166static void WindowSizeCallback(GLFWwindow *window, int width, int height)
5167{
5168 // Reset viewport and projection matrix for new size
5169 SetupViewport(width, height);
5170
5171 CORE.Window.currentFbo.width = width;
5172 CORE.Window.currentFbo.height = height;
5173 CORE.Window.resizedLastFrame = true;
5174
5175 if (IsWindowFullscreen()) return;
5176
5177 // Set current screen size
5178#if defined(__APPLE__)
5179 CORE.Window.screen.width = width;
5180 CORE.Window.screen.height = height;
5181#else
5182 if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
5183 {
5184 Vector2 windowScaleDPI = GetWindowScaleDPI();
5185
5186 CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x);
5187 CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y);
5188 }
5189 else
5190 {
5191 CORE.Window.screen.width = width;
5192 CORE.Window.screen.height = height;
5193 }
5194#endif
5195
5196 // NOTE: Postprocessing texture is not scaled to new size
5197}
5198
5199// GLFW3 WindowIconify Callback, runs when window is minimized/restored
5200static void WindowIconifyCallback(GLFWwindow *window, int iconified)
5201{
5202 if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
5203 else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
5204}
5205
5206#if !defined(PLATFORM_WEB)
5207// GLFW3 WindowMaximize Callback, runs when window is maximized/restored
5208static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
5209{
5210 if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized
5211 else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored
5212}
5213#endif
5214
5215// GLFW3 WindowFocus Callback, runs when window get/lose focus
5216static void WindowFocusCallback(GLFWwindow *window, int focused)
5217{
5218 if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused
5219 else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus
5220}
5221
5222// GLFW3 Keyboard Callback, runs on key pressed
5223static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
5224{
5225 // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
5226 // to work properly with our implementation (IsKeyDown/IsKeyUp checks)
5227 if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
5228 else CORE.Input.Keyboard.currentKeyState[key] = 1;
5229
5230 // Check if there is space available in the key queue
5232 {
5233 // Add character to the queue
5236 }
5237
5238 // Check the exit key to set close window
5239 if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE);
5240
5241#if defined(SUPPORT_SCREEN_CAPTURE)
5242 if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS))
5243 {
5244#if defined(SUPPORT_GIF_RECORDING)
5245 if (mods == GLFW_MOD_CONTROL)
5246 {
5247 if (gifRecording)
5248 {
5249 gifRecording = false;
5250
5251 MsfGifResult result = msf_gif_end(&gifState);
5252
5253 SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize);
5254 msf_gif_free(result);
5255
5256 #if defined(PLATFORM_WEB)
5257 // Download file from MEMFS (emscripten memory filesystem)
5258 // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html
5259 emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1)));
5260 #endif
5261
5262 TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording");
5263 }
5264 else
5265 {
5266 gifRecording = true;
5267 gifFrameCounter = 0;
5268
5269 msf_gif_begin(&gifState, CORE.Window.screen.width, CORE.Window.screen.height);
5270 screenshotCounter++;
5271
5272 TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter));
5273 }
5274 }
5275 else
5276#endif // SUPPORT_GIF_RECORDING
5277 {
5278 TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
5279 screenshotCounter++;
5280 }
5281 }
5282#endif // SUPPORT_SCREEN_CAPTURE
5283
5284#if defined(SUPPORT_EVENTS_AUTOMATION)
5285 if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS))
5286 {
5287 eventsRecording = !eventsRecording;
5288
5289 // On finish recording, we export events into a file
5290 if (!eventsRecording) ExportAutomationEvents("eventsrec.rep");
5291 }
5292 else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS))
5293 {
5294 LoadAutomationEvents("eventsrec.rep");
5295 eventsPlaying = true;
5296
5297 TRACELOG(LOG_WARNING, "eventsPlaying enabled!");
5298 }
5299#endif
5300}
5301
5302// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value)
5303static void CharCallback(GLFWwindow *window, unsigned int key)
5304{
5305 //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
5306
5307 // NOTE: Registers any key down considering OS keyboard layout but
5308 // do not detects action events, those should be managed by user...
5309 // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
5310 // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
5311
5312 // Check if there is space available in the queue
5314 {
5315 // Add character to the queue
5318 }
5319}
5320
5321// GLFW3 Mouse Button Callback, runs on mouse button pressed
5322static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
5323{
5324 // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
5325 // but future releases may add more actions (i.e. GLFW_REPEAT)
5326 CORE.Input.Mouse.currentButtonState[button] = action;
5327
5328#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP
5329 // Process mouse events as touches to be able to use mouse-gestures
5330 GestureEvent gestureEvent = { 0 };
5331
5332 // Register touch actions
5333 if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
5334 else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP;
5335
5336 // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback()
5337
5338 // Assign a pointer ID
5339 gestureEvent.pointId[0] = 0;
5340
5341 // Register touch points count
5342 gestureEvent.pointCount = 1;
5343
5344 // Register touch points position, only one point registered
5345 gestureEvent.position[0] = GetMousePosition();
5346
5347 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
5348 gestureEvent.position[0].x /= (float)GetScreenWidth();
5349 gestureEvent.position[0].y /= (float)GetScreenHeight();
5350
5351 // Gesture data is sent to gestures system for processing
5352 ProcessGestureEvent(gestureEvent);
5353#endif
5354}
5355
5356// GLFW3 Cursor Position Callback, runs on mouse move
5357static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
5358{
5359 CORE.Input.Mouse.currentPosition.x = (float)x;
5360 CORE.Input.Mouse.currentPosition.y = (float)y;
5362
5363#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP
5364 // Process mouse events as touches to be able to use mouse-gestures
5365 GestureEvent gestureEvent = { 0 };
5366
5367 gestureEvent.touchAction = TOUCH_ACTION_MOVE;
5368
5369 // Assign a pointer ID
5370 gestureEvent.pointId[0] = 0;
5371
5372 // Register touch points count
5373 gestureEvent.pointCount = 1;
5374
5375 // Register touch points position, only one point registered
5376 gestureEvent.position[0] = CORE.Input.Touch.position[0];
5377
5378 // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
5379 gestureEvent.position[0].x /= (float)GetScreenWidth();
5380 gestureEvent.position[0].y /= (float)GetScreenHeight();
5381
5382 // Gesture data is sent to gestures system for processing
5383 ProcessGestureEvent(gestureEvent);
5384#endif
5385}
5386
5387// GLFW3 Scrolling Callback, runs on mouse wheel
5388static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
5389{
5390 if (fabs(xoffset) > fabs(yoffset)) CORE.Input.Mouse.currentWheelMove = (float)xoffset;
5391 else CORE.Input.Mouse.currentWheelMove = (float)yoffset;
5392}
5393
5394// GLFW3 CursorEnter Callback, when cursor enters the window
5395static void CursorEnterCallback(GLFWwindow *window, int enter)
5396{
5397 if (enter == true) CORE.Input.Mouse.cursorOnScreen = true;
5398 else CORE.Input.Mouse.cursorOnScreen = false;
5399}
5400
5401// GLFW3 Window Drop Callback, runs when drop files into window
5402// NOTE: Paths are stored in dynamic memory for further retrieval
5403// Everytime new files are dropped, old ones are discarded
5404static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
5405{
5407
5408 CORE.Window.dropFilesPath = (char **)RL_MALLOC(count*sizeof(char *));
5409
5410 for (int i = 0; i < count; i++)
5411 {
5412 CORE.Window.dropFilesPath[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char));
5413 strcpy(CORE.Window.dropFilesPath[i], paths[i]);
5414 }
5415
5416 CORE.Window.dropFileCount = count;
5417}
5418#endif
5419
5420#if defined(PLATFORM_ANDROID)
5421// ANDROID: Process activity lifecycle commands
5422static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
5423{
5424 switch (cmd)
5425 {
5426 case APP_CMD_START:
5427 {
5428 //rendering = true;
5429 } break;
5430 case APP_CMD_RESUME: break;
5431 case APP_CMD_INIT_WINDOW:
5432 {
5433 if (app->window != NULL)
5434 {
5435 if (CORE.Android.contextRebindRequired)
5436 {
5437 // Reset screen scaling to full display size
5438 EGLint displayFormat = 0;
5439 eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
5440 ANativeWindow_setBuffersGeometry(app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat);
5441
5442 // Recreate display surface and re-attach OpenGL context
5443 CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL);
5444 eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context);
5445
5446 CORE.Android.contextRebindRequired = false;
5447 }
5448 else
5449 {
5450 CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window);
5451 CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window);
5452
5453 // Initialize graphics device (display device and OpenGL context)
5454 InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height);
5455
5456 // Initialize hi-res timer
5457 InitTimer();
5458
5459 // Initialize random seed
5460 srand((unsigned int)time(NULL));
5461
5462 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
5463 // Load default font
5464 // WARNING: External function: Module required: rtext
5466 Rectangle rec = GetFontDefault().recs[95];
5467 // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
5468 #if defined(SUPPORT_MODULE_RSHAPES)
5469 SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes
5470 #endif
5471 #endif
5472
5473 // TODO: GPU assets reload in case of lost focus (lost context)
5474 // NOTE: This problem has been solved just unbinding and rebinding context from display
5475 /*
5476 if (assetsReloadRequired)
5477 {
5478 for (int i = 0; i < assetCount; i++)
5479 {
5480 // TODO: Unload old asset if required
5481
5482 // Load texture again to pointed texture
5483 (*textureAsset + i) = LoadTexture(assetPath[i]);
5484 }
5485 }
5486 */
5487 }
5488 }
5489 } break;
5490 case APP_CMD_GAINED_FOCUS:
5491 {
5492 CORE.Android.appEnabled = true;
5493 //ResumeMusicStream();
5494 } break;
5495 case APP_CMD_PAUSE: break;
5496 case APP_CMD_LOST_FOCUS:
5497 {
5498 CORE.Android.appEnabled = false;
5499 //PauseMusicStream();
5500 } break;
5501 case APP_CMD_TERM_WINDOW:
5502 {
5503 // Dettach OpenGL context and destroy display surface
5504 // NOTE 1: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
5505 // NOTE 2: In some cases (too many context loaded), OS could unload context automatically... :(
5507 eglDestroySurface(CORE.Window.device, CORE.Window.surface);
5508
5509 CORE.Android.contextRebindRequired = true;
5510 } break;
5511 case APP_CMD_SAVE_STATE: break;
5512 case APP_CMD_STOP: break;
5513 case APP_CMD_DESTROY:
5514 {
5515 // TODO: Finish activity?
5516 //ANativeActivity_finish(CORE.Android.app->activity);
5517 } break;
5518 case APP_CMD_CONFIG_CHANGED:
5519 {
5520 //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager);
5521 //print_cur_config(CORE.Android.app);
5522
5523 // Check screen orientation here!
5524 } break;
5525 default: break;
5526 }
5527}
5528
5529// ANDROID: Get input events
5530static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
5531{
5532 // If additional inputs are required check:
5533 // https://developer.android.com/ndk/reference/group/input
5534 // https://developer.android.com/training/game-controllers/controller-input
5535
5536 int type = AInputEvent_getType(event);
5537 int source = AInputEvent_getSource(event);
5538
5539 if (type == AINPUT_EVENT_TYPE_MOTION)
5540 {
5541 if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
5542 ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
5543 {
5544 // Get first touch position
5545 CORE.Input.Touch.position[0].x = AMotionEvent_getX(event, 0);
5546 CORE.Input.Touch.position[0].y = AMotionEvent_getY(event, 0);
5547
5548 // Get second touch position
5549 CORE.Input.Touch.position[1].x = AMotionEvent_getX(event, 1);
5550 CORE.Input.Touch.position[1].y = AMotionEvent_getY(event, 1);
5551
5552 int32_t keycode = AKeyEvent_getKeyCode(event);
5553 if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
5554 {
5555 CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down
5556
5559 }
5560 else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up
5561
5562 // Stop processing gamepad buttons
5563 return 1;
5564 }
5565 }
5566 else if (type == AINPUT_EVENT_TYPE_KEY)
5567 {
5568 int32_t keycode = AKeyEvent_getKeyCode(event);
5569 //int32_t AKeyEvent_getMetaState(event);
5570
5571 // Save current button and its state
5572 // NOTE: Android key action is 0 for down and 1 for up
5573 if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN)
5574 {
5575 CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down
5576
5579 }
5580 else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up
5581
5582 if (keycode == AKEYCODE_POWER)
5583 {
5584 // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS
5585 // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS
5586 // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected.
5587 // NOTE: AndroidManifest.xml must have <activity android:configChanges="orientation|keyboardHidden|screenSize" >
5588 // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
5589 return 0;
5590 }
5591 else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU))
5592 {
5593 // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS!
5594 return 1;
5595 }
5596 else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN))
5597 {
5598 // Set default OS behaviour
5599 return 0;
5600 }
5601
5602 return 0;
5603 }
5604
5605 // Register touch points count
5606 CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event);
5607
5608 for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
5609 {
5610 // Register touch points id
5611 CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i);
5612
5613 // Register touch points position
5614 CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
5615
5616 // Normalize CORE.Input.Touch.position[x] for screenWidth and screenHeight
5617 CORE.Input.Touch.position[i].x /= (float)GetScreenWidth();
5618 CORE.Input.Touch.position[i].y /= (float)GetScreenHeight();
5619 }
5620
5621 int32_t action = AMotionEvent_getAction(event);
5622 unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
5623
5624 if (flags == AMOTION_EVENT_ACTION_DOWN || flags == AMOTION_EVENT_ACTION_MOVE) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
5625 else if (flags == AMOTION_EVENT_ACTION_UP) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
5626
5627#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID
5628 GestureEvent gestureEvent = { 0 };
5629
5630 gestureEvent.pointCount = CORE.Input.Touch.pointCount;
5631
5632 // Register touch actions
5633 if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
5634 else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP;
5635 else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
5636 else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
5637
5638 for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
5639 {
5640 gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
5641 gestureEvent.position[i] = CORE.Input.Touch.position[i];
5642 }
5643
5644 // Gesture data is sent to gestures system for processing
5645 ProcessGestureEvent(gestureEvent);
5646#endif
5647
5648 return 0;
5649}
5650#endif
5651
5652#if defined(PLATFORM_WEB)
5653// Register fullscreen change events
5654static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData)
5655{
5656 // TODO.
5657
5658 return 1; // The event was consumed by the callback handler
5659}
5660
5661// Register window resize event
5662static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
5663{
5664 // TODO.
5665
5666 return 1; // The event was consumed by the callback handler
5667}
5668
5669EM_JS(int, GetCanvasWidth, (), { return canvas.clientWidth; });
5670EM_JS(int, GetCanvasHeight, (), { return canvas.clientHeight; });
5671
5672// Register DOM element resize event
5673static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
5674{
5675 // Don't resize non-resizeable windows
5676 if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1;
5677
5678 // This event is called whenever the window changes sizes,
5679 // so the size of the canvas object is explicitly retrieved below
5680 int width = GetCanvasWidth();
5681 int height = GetCanvasHeight();
5682 emscripten_set_canvas_element_size("#canvas",width,height);
5683
5684 SetupViewport(width, height); // Reset viewport and projection matrix for new size
5685
5686 CORE.Window.currentFbo.width = width;
5687 CORE.Window.currentFbo.height = height;
5688 CORE.Window.resizedLastFrame = true;
5689
5690 if (IsWindowFullscreen()) return 1;
5691
5692 // Set current screen size
5693 CORE.Window.screen.width = width;
5694 CORE.Window.screen.height = height;
5695
5696 // NOTE: Postprocessing texture is not scaled to new size
5697
5698 return 0;
5699}
5700
5701// Register mouse input events
5702static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
5703{
5704 // This is only for registering mouse click events with emscripten and doesn't need to do anything
5705
5706 return 1; // The event was consumed by the callback handler
5707}
5708
5709// Register connected/disconnected gamepads events
5710static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
5711{
5712 /*
5713 TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"",
5714 eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
5715 gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
5716
5717 for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
5718 for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
5719 */
5720
5721 if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS))
5722 {
5723 CORE.Input.Gamepad.ready[gamepadEvent->index] = true;
5724 sprintf(CORE.Input.Gamepad.name[gamepadEvent->index],"%s",gamepadEvent->id);
5725 }
5726 else CORE.Input.Gamepad.ready[gamepadEvent->index] = false;
5727
5728 return 1; // The event was consumed by the callback handler
5729}
5730
5731// Register touch input events
5732static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
5733{
5734 // Register touch points count
5735 CORE.Input.Touch.pointCount = touchEvent->numTouches;
5736
5737 double canvasWidth = 0.0;
5738 double canvasHeight = 0.0;
5739 // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but
5740 // we are looking for actual CSS size: canvas.style.width and canvas.style.height
5741 //EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight);
5742 emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight);
5743
5744 for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
5745 {
5746 // Register touch points id
5747 CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier;
5748
5749 // Register touch points position
5750 CORE.Input.Touch.position[i] = (Vector2){ touchEvent->touches[i].targetX, touchEvent->touches[i].targetY };
5751
5752 // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height
5753 CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth);
5754 CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight);
5755
5756 if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1;
5757 else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
5758 }
5759
5760#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB
5761 GestureEvent gestureEvent = { 0 };
5762
5763 gestureEvent.pointCount = CORE.Input.Touch.pointCount;
5764
5765 // Register touch actions
5766 if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
5767 else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP;
5768 else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
5769 else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
5770
5771 for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
5772 {
5773 gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
5774 gestureEvent.position[i] = CORE.Input.Touch.position[i];
5775 }
5776
5777 // Gesture data is sent to gestures system for processing
5778 ProcessGestureEvent(gestureEvent);
5779#endif
5780
5781 return 1; // The event was consumed by the callback handler
5782}
5783#endif
5784
5785#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM)
5786// Initialize Keyboard system (using standard input)
5787static void InitKeyboard(void)
5788{
5789 // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor,
5790 // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
5791
5792 // Save terminal keyboard settings
5793 tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings);
5794
5795 // Reconfigure terminal with new settings
5796 struct termios keyboardNewSettings = { 0 };
5797 keyboardNewSettings = CORE.Input.Keyboard.defaultSettings;
5798
5799 // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
5800 // NOTE: ISIG controls if ^C and ^Z generate break signals or not
5801 keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG);
5802 //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
5803 keyboardNewSettings.c_cc[VMIN] = 1;
5804 keyboardNewSettings.c_cc[VTIME] = 0;
5805
5806 // Set new keyboard settings (change occurs immediately)
5807 tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings);
5808
5809 // Save old keyboard mode to restore it at the end
5810 CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags
5811 fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified
5812
5813 // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno)
5814 int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode);
5815
5816 // In case of failure, it could mean a remote keyboard is used (SSH)
5817 if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used");
5818 else
5819 {
5820 // Reconfigure keyboard mode to get:
5821 // - scancodes (K_RAW)
5822 // - keycodes (K_MEDIUMRAW)
5823 // - ASCII chars (K_XLATE)
5824 // - UNICODE chars (K_UNICODE)
5825 ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); // ASCII chars
5826 }
5827
5828 // Register keyboard restore when program finishes
5829 atexit(RestoreKeyboard);
5830}
5831
5832// Restore default keyboard input
5833static void RestoreKeyboard(void)
5834{
5835 // Reset to default keyboard settings
5836 tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings);
5837
5838 // Reconfigure keyboard to default mode
5839 fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags);
5840 ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode);
5841}
5842
5843#if defined(SUPPORT_SSH_KEYBOARD_RPI)
5844// Process keyboard inputs
5845static void ProcessKeyboard(void)
5846{
5847 #define MAX_KEYBUFFER_SIZE 32 // Max size in bytes to read
5848
5849 // Keyboard input polling (fill keys[256] array with status)
5850 int bufferByteCount = 0; // Bytes available on the buffer
5851 char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 }; // Max keys to be read at a time
5852
5853 // Read availables keycodes from stdin
5854 bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE); // POSIX system call
5855
5856 // Reset pressed keys array (it will be filled below)
5857 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.currentKeyState[i] = 0;
5858
5859 // Fill all read bytes (looking for keys)
5860 for (int i = 0; i < bufferByteCount; i++)
5861 {
5862 // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code!
5863 // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42
5864 if (keysBuffer[i] == 0x1b)
5865 {
5866 // Check if ESCAPE key has been pressed to stop program
5867 if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1;
5868 else
5869 {
5870 if (keysBuffer[i + 1] == 0x5b) // Special function key
5871 {
5872 if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32))
5873 {
5874 // Process special function keys (F1 - F12)
5875 switch (keysBuffer[i + 3])
5876 {
5877 case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break; // raylib KEY_F1
5878 case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break; // raylib KEY_F2
5879 case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break; // raylib KEY_F3
5880 case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break; // raylib KEY_F4
5881 case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break; // raylib KEY_F5
5882 case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break; // raylib KEY_F6
5883 case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break; // raylib KEY_F7
5884 case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break; // raylib KEY_F8
5885 case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break; // raylib KEY_F9
5886 case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break; // raylib KEY_F10
5887 case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break; // raylib KEY_F11
5888 case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break; // raylib KEY_F12
5889 default: break;
5890 }
5891
5892 if (keysBuffer[i + 2] == 0x5b) i += 4;
5893 else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5;
5894 }
5895 else
5896 {
5897 switch (keysBuffer[i + 2])
5898 {
5899 case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break; // raylib KEY_UP
5900 case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break; // raylib KEY_DOWN
5901 case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break; // raylib KEY_RIGHT
5902 case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break; // raylib KEY_LEFT
5903 default: break;
5904 }
5905
5906 i += 3; // Jump to next key
5907 }
5908
5909 // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT)
5910 }
5911 }
5912 }
5913 else if (keysBuffer[i] == 0x0a) // raylib KEY_ENTER (don't mix with <linux/input.h> KEY_*)
5914 {
5915 CORE.Input.Keyboard.currentKeyState[257] = 1;
5916
5917 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue
5919 }
5920 else if (keysBuffer[i] == 0x7f) // raylib KEY_BACKSPACE
5921 {
5922 CORE.Input.Keyboard.currentKeyState[259] = 1;
5923
5924 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue
5926 }
5927 else
5928 {
5929 // Translate lowercase a-z letters to A-Z
5930 if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122))
5931 {
5932 CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1;
5933 }
5934 else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1;
5935
5936 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i]; // Add keys pressed into queue
5938 }
5939 }
5940
5941 // Check exit key (same functionality as GLFW3 KeyCallback())
5942 if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
5943
5944#if defined(SUPPORT_SCREEN_CAPTURE)
5945 // Check screen capture key (raylib key: KEY_F12)
5946 if (CORE.Input.Keyboard.currentKeyState[301] == 1)
5947 {
5948 TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
5949 screenshotCounter++;
5950 }
5951#endif
5952}
5953#endif // SUPPORT_SSH_KEYBOARD_RPI
5954
5955// Initialise user input from evdev(/dev/input/event<N>) this means mouse, keyboard or gamepad devices
5956static void InitEvdevInput(void)
5957{
5958 char path[MAX_FILEPATH_LENGTH] = { 0 };
5959 DIR *directory = NULL;
5960 struct dirent *entity = NULL;
5961
5962 // Initialise keyboard file descriptor
5963 CORE.Input.Keyboard.fd = -1;
5964
5965 // Reset variables
5966 for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
5967 {
5968 CORE.Input.Touch.position[i].x = -1;
5969 CORE.Input.Touch.position[i].y = -1;
5970 }
5971
5972 // Reset keyboard key state
5973 for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.currentKeyState[i] = 0;
5974
5975 // Open the linux directory of "/dev/input"
5976 directory = opendir(DEFAULT_EVDEV_PATH);
5977
5978 if (directory)
5979 {
5980 while ((entity = readdir(directory)) != NULL)
5981 {
5982 if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*"
5983 (strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*"
5984 {
5985 sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
5986 ConfigureEvdevDevice(path); // Configure the device if appropriate
5987 }
5988 }
5989
5990 closedir(directory);
5991 }
5992 else TRACELOG(LOG_WARNING, "RPI: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH);
5993}
5994
5995// Identifies a input device and configures it for use if appropriate
5996static void ConfigureEvdevDevice(char *device)
5997{
5998 #define BITS_PER_LONG (8*sizeof(long))
5999 #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1)
6000 #define OFF(x) ((x)%BITS_PER_LONG)
6001 #define BIT(x) (1UL<<OFF(x))
6002 #define LONG(x) ((x)/BITS_PER_LONG)
6003 #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
6004
6005 struct input_absinfo absinfo = { 0 };
6006 unsigned long evBits[NBITS(EV_MAX)] = { 0 };
6007 unsigned long absBits[NBITS(ABS_MAX)] = { 0 };
6008 unsigned long relBits[NBITS(REL_MAX)] = { 0 };
6009 unsigned long keyBits[NBITS(KEY_MAX)] = { 0 };
6010 bool hasAbs = false;
6011 bool hasRel = false;
6012 bool hasAbsMulti = false;
6013 int freeWorkerId = -1;
6014 int fd = -1;
6015
6016 InputEventWorker *worker = NULL;
6017
6018 // Open the device and allocate worker
6019 //-------------------------------------------------------------------------------------------------------
6020 // Find a free spot in the workers array
6021 for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
6022 {
6023 if (CORE.Input.eventWorker[i].threadId == 0)
6024 {
6025 freeWorkerId = i;
6026 break;
6027 }
6028 }
6029
6030 // Select the free worker from array
6031 if (freeWorkerId >= 0)
6032 {
6033 worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker
6034 memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker
6035 }
6036 else
6037 {
6038 TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device);
6039 return;
6040 }
6041
6042 // Open the device
6043 fd = open(device, O_RDONLY | O_NONBLOCK);
6044 if (fd < 0)
6045 {
6046 TRACELOG(LOG_WARNING, "RPI: Failed to open input device %s", device);
6047 return;
6048 }
6049 worker->fd = fd;
6050
6051 // Grab number on the end of the devices name "event<N>"
6052 int devNum = 0;
6053 char *ptrDevName = strrchr(device, 't');
6054 worker->eventNum = -1;
6055
6056 if (ptrDevName != NULL)
6057 {
6058 if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum;
6059 }
6060
6061 // At this point we have a connection to the device, but we don't yet know what the device is.
6062 // It could be many things, even as simple as a power button...
6063 //-------------------------------------------------------------------------------------------------------
6064
6065 // Identify the device
6066 //-------------------------------------------------------------------------------------------------------
6067 ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties
6068
6069 // Check for absolute input devices
6070 if (TEST_BIT(evBits, EV_ABS))
6071 {
6072 ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
6073
6074 // Check for absolute movement support (usualy touchscreens, but also joysticks)
6075 if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y))
6076 {
6077 hasAbs = true;
6078
6079 // Get the scaling values
6080 ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
6081 worker->absRange.x = absinfo.minimum;
6082 worker->absRange.width = absinfo.maximum - absinfo.minimum;
6083 ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
6084 worker->absRange.y = absinfo.minimum;
6085 worker->absRange.height = absinfo.maximum - absinfo.minimum;
6086 }
6087
6088 // Check for multiple absolute movement support (usualy multitouch touchscreens)
6089 if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y))
6090 {
6091 hasAbsMulti = true;
6092
6093 // Get the scaling values
6094 ioctl(fd, EVIOCGABS(ABS_X), &absinfo);
6095 worker->absRange.x = absinfo.minimum;
6096 worker->absRange.width = absinfo.maximum - absinfo.minimum;
6097 ioctl(fd, EVIOCGABS(ABS_Y), &absinfo);
6098 worker->absRange.y = absinfo.minimum;
6099 worker->absRange.height = absinfo.maximum - absinfo.minimum;
6100 }
6101 }
6102
6103 // Check for relative movement support (usualy mouse)
6104 if (TEST_BIT(evBits, EV_REL))
6105 {
6106 ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
6107
6108 if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true;
6109 }
6110
6111 // Check for button support to determine the device type(usualy on all input devices)
6112 if (TEST_BIT(evBits, EV_KEY))
6113 {
6114 ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
6115
6116 if (hasAbs || hasAbsMulti)
6117 {
6118 if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen
6119 if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet
6120 if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet
6121 if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet
6122 if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device
6123 }
6124
6125 if (hasRel)
6126 {
6127 if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse
6128 if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse
6129 }
6130
6131 if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad
6132 if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad
6133 if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad
6134 if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad
6135 if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad
6136
6137 if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard
6138 }
6139 //-------------------------------------------------------------------------------------------------------
6140
6141 // Decide what to do with the device
6142 //-------------------------------------------------------------------------------------------------------
6143 if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1))
6144 {
6145 // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a
6146 // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate
6147 // threads so that they don't drop events when the frame rate is slow.
6148 TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device);
6149 CORE.Input.Keyboard.fd = worker->fd;
6150 }
6151 else if (worker->isTouch || worker->isMouse)
6152 {
6153 // Looks like an interesting device
6154 TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device,
6155 worker->isMouse? "mouse " : "",
6156 worker->isMultitouch? "multitouch " : "",
6157 worker->isTouch? "touchscreen " : "",
6158 worker->isGamepad? "gamepad " : "");
6159
6160 // Create a thread for this device
6161 int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker);
6162 if (error != 0)
6163 {
6164 TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error);
6165 worker->threadId = 0;
6166 close(fd);
6167 }
6168
6169#if defined(USE_LAST_TOUCH_DEVICE)
6170 // Find touchscreen with the highest index
6171 int maxTouchNumber = -1;
6172
6173 for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
6174 {
6175 if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum;
6176 }
6177
6178 // Find touchscreens with lower indexes
6179 for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i)
6180 {
6181 if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber))
6182 {
6183 if (CORE.Input.eventWorker[i].threadId != 0)
6184 {
6185 TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i);
6186 pthread_cancel(CORE.Input.eventWorker[i].threadId);
6187 close(CORE.Input.eventWorker[i].fd);
6188 }
6189 }
6190 }
6191#endif
6192 }
6193 else close(fd); // We are not interested in this device
6194 //-------------------------------------------------------------------------------------------------------
6195}
6196
6197static void PollKeyboardEvents(void)
6198{
6199 // Scancode to keycode mapping for US keyboards
6200 // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard:
6201 // Currently non US keyboards will have the wrong mapping for some keys
6202 static const int keymapUS[] = {
6203 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84,
6204 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96,
6205 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291,
6206 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325,
6207 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95,
6208 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261,
6209 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127,
6210 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
6211 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
6212 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
6213 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
6214 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
6215 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226,
6216 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
6217 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0
6218 };
6219
6220 int fd = CORE.Input.Keyboard.fd;
6221 if (fd == -1) return;
6222
6223 struct input_event event = { 0 };
6224 int keycode = -1;
6225
6226 // Try to read data from the keyboard and only continue if successful
6227 while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
6228 {
6229 // Button parsing
6230 if (event.type == EV_KEY)
6231 {
6232#if defined(SUPPORT_SSH_KEYBOARD_RPI)
6233 // Change keyboard mode to events
6234 CORE.Input.Keyboard.evtMode = true;
6235#endif
6236 // Keyboard button parsing
6237 if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255
6238 {
6239 keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode
6240
6241 // Make sure we got a valid keycode
6242 if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState)))
6243 {
6244 // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
6245 // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL,
6246 // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat
6247 CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0;
6248 if (event.value >= 1)
6249 {
6250 CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed
6252 }
6253
6254 #if defined(SUPPORT_SCREEN_CAPTURE)
6255 // Check screen capture key (raylib key: KEY_F12)
6256 if (CORE.Input.Keyboard.currentKeyState[301] == 1)
6257 {
6258 TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
6259 screenshotCounter++;
6260 }
6261 #endif
6262
6263 if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
6264
6265 TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", event.value == 0 ? "UP":"DOWN", event.code, keycode);
6266 }
6267 }
6268 }
6269 }
6270}
6271
6272// Input device events reading thread
6273static void *EventThread(void *arg)
6274{
6275 struct input_event event = { 0 };
6276 InputEventWorker *worker = (InputEventWorker *)arg;
6277
6278 int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
6279 bool gestureUpdate = false; // Flag to note gestures require to update
6280
6281 while (!CORE.Window.shouldClose)
6282 {
6283 // Try to read data from the device and only continue if successful
6284 while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event))
6285 {
6286 // Relative movement parsing
6287 if (event.type == EV_REL)
6288 {
6289 if (event.code == REL_X)
6290 {
6291 CORE.Input.Mouse.currentPosition.x += event.value;
6293
6294 touchAction = 2; // TOUCH_ACTION_MOVE
6295 gestureUpdate = true;
6296 }
6297
6298 if (event.code == REL_Y)
6299 {
6300 CORE.Input.Mouse.currentPosition.y += event.value;
6302
6303 touchAction = 2; // TOUCH_ACTION_MOVE
6304 gestureUpdate = true;
6305 }
6306
6307 if (event.code == REL_WHEEL) CORE.Input.Mouse.currentWheelMove += event.value;
6308 }
6309
6310 // Absolute movement parsing
6311 if (event.type == EV_ABS)
6312 {
6313 // Basic movement
6314 if (event.code == ABS_X)
6315 {
6316 CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange
6317 CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange
6318
6319 touchAction = 2; // TOUCH_ACTION_MOVE
6320 gestureUpdate = true;
6321 }
6322
6323 if (event.code == ABS_Y)
6324 {
6325 CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange
6326 CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange
6327
6328 touchAction = 2; // TOUCH_ACTION_MOVE
6329 gestureUpdate = true;
6330 }
6331
6332 // Multitouch movement
6333 if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remember the slot number for the folowing events
6334
6335 if (event.code == ABS_MT_POSITION_X)
6336 {
6337 if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange
6338 }
6339
6340 if (event.code == ABS_MT_POSITION_Y)
6341 {
6342 if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange
6343 }
6344
6345 if (event.code == ABS_MT_TRACKING_ID)
6346 {
6347 if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS))
6348 {
6349 // Touch has ended for this point
6350 CORE.Input.Touch.position[worker->touchSlot].x = -1;
6351 CORE.Input.Touch.position[worker->touchSlot].y = -1;
6352 }
6353 }
6354
6355 // Touchscreen tap
6356 if (event.code == ABS_PRESSURE)
6357 {
6358 int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
6359
6360 if (!event.value && previousMouseLeftButtonState)
6361 {
6362 CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
6363
6364 touchAction = 0; // TOUCH_ACTION_UP
6365 gestureUpdate = true;
6366 }
6367
6368 if (event.value && !previousMouseLeftButtonState)
6369 {
6370 CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
6371
6372 touchAction = 1; // TOUCH_ACTION_DOWN
6373 gestureUpdate = true;
6374 }
6375 }
6376
6377 }
6378
6379 // Button parsing
6380 if (event.type == EV_KEY)
6381 {
6382 // Mouse button parsing
6383 if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
6384 {
6385 CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
6386
6387 if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN
6388 else touchAction = 0; // TOUCH_ACTION_UP
6389 gestureUpdate = true;
6390 }
6391
6392 if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
6393 if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value;
6394 if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value;
6395 if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value;
6396 if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value;
6397 if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value;
6398 }
6399
6400 // Screen confinement
6401 if (!CORE.Input.Mouse.cursorHidden)
6402 {
6403 if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
6405
6406 if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
6408 }
6409
6410#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_RPI, PLATFORM_DRM
6411 if (gestureUpdate)
6412 {
6413 GestureEvent gestureEvent = { 0 };
6414
6415 gestureEvent.pointCount = 0;
6416 gestureEvent.touchAction = touchAction;
6417
6418 if (CORE.Input.Touch.position[0].x >= 0) gestureEvent.pointCount++;
6419 if (CORE.Input.Touch.position[1].x >= 0) gestureEvent.pointCount++;
6420 if (CORE.Input.Touch.position[2].x >= 0) gestureEvent.pointCount++;
6421 if (CORE.Input.Touch.position[3].x >= 0) gestureEvent.pointCount++;
6422
6423 gestureEvent.pointId[0] = 0;
6424 gestureEvent.pointId[1] = 1;
6425 gestureEvent.pointId[2] = 2;
6426 gestureEvent.pointId[3] = 3;
6427
6428 gestureEvent.position[0] = CORE.Input.Touch.position[0];
6429 gestureEvent.position[1] = CORE.Input.Touch.position[1];
6430 gestureEvent.position[2] = CORE.Input.Touch.position[2];
6431 gestureEvent.position[3] = CORE.Input.Touch.position[3];
6432
6433 ProcessGestureEvent(gestureEvent);
6434 }
6435#endif
6436 }
6437
6438 WaitTime(5); // Sleep for 5ms to avoid hogging CPU time
6439 }
6440
6441 close(worker->fd);
6442
6443 return NULL;
6444}
6445
6446// Initialize gamepad system
6447static void InitGamepad(void)
6448{
6449 char gamepadDev[128] = { 0 };
6450
6451 for (int i = 0; i < MAX_GAMEPADS; i++)
6452 {
6453 sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i);
6454
6455 if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0)
6456 {
6457 // NOTE: Only show message for first gamepad
6458 if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available");
6459 }
6460 else
6461 {
6462 CORE.Input.Gamepad.ready[i] = true;
6463
6464 // NOTE: Only create one thread
6465 if (i == 0)
6466 {
6467 int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL);
6468
6469 if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread");
6470 else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully");
6471 }
6472 }
6473 }
6474}
6475
6476// Process Gamepad (/dev/input/js0)
6477static void *GamepadThread(void *arg)
6478{
6479 #define JS_EVENT_BUTTON 0x01 // Button pressed/released
6480 #define JS_EVENT_AXIS 0x02 // Joystick axis moved
6481 #define JS_EVENT_INIT 0x80 // Initial state of device
6482
6483 struct js_event {
6484 unsigned int time; // event timestamp in milliseconds
6485 short value; // event value
6486 unsigned char type; // event type
6487 unsigned char number; // event axis/button number
6488 };
6489
6490 // Read gamepad event
6491 struct js_event gamepadEvent = { 0 };
6492
6493 while (!CORE.Window.shouldClose)
6494 {
6495 for (int i = 0; i < MAX_GAMEPADS; i++)
6496 {
6497 if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event))
6498 {
6499 gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events
6500
6501 // Process gamepad events by type
6502 if (gamepadEvent.type == JS_EVENT_BUTTON)
6503 {
6504 //TRACELOG(LOG_WARNING, "RPI: Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
6505
6506 if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
6507 {
6508 // 1 - button pressed, 0 - button released
6509 CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value;
6510
6511 if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number;
6512 else CORE.Input.Gamepad.lastButtonPressed = -1;
6513 }
6514 }
6515 else if (gamepadEvent.type == JS_EVENT_AXIS)
6516 {
6517 //TRACELOG(LOG_WARNING, "RPI: Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value);
6518
6519 if (gamepadEvent.number < MAX_GAMEPAD_AXIS)
6520 {
6521 // NOTE: Scaling of gamepadEvent.value to get values between -1..1
6522 CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768;
6523 }
6524 }
6525 }
6526 else WaitTime(1); // Sleep for 1 ms to avoid hogging CPU time
6527 }
6528 }
6529
6530 return NULL;
6531}
6532#endif // PLATFORM_RPI || PLATFORM_DRM
6533
6534#if defined(PLATFORM_DRM)
6535// Search matching DRM mode in connector's mode list
6536static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode)
6537{
6538 if (NULL == connector) return -1;
6539 if (NULL == mode) return -1;
6540
6541 // safe bitwise comparison of two modes
6542 #define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b)) ? sizeof(a) : sizeof(b))
6543
6544 for (size_t i = 0; i < connector->count_modes; i++)
6545 {
6546 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
6547 connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
6548
6549 if (0 == BINCMP(&CORE.Window.crtc->mode, &CORE.Window.connector->modes[i])) return i;
6550 }
6551
6552 return -1;
6553
6554 #undef BINCMP
6555}
6556
6557// Search exactly matching DRM connector mode in connector's list
6558static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
6559{
6560 TRACELOG(LOG_TRACE, "DISPLAY: Searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no");
6561
6562 if (NULL == connector) return -1;
6563
6564 for (int i = 0; i < CORE.Window.connector->count_modes; i++)
6565 {
6566 const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
6567
6568 TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
6569
6570 if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) continue;
6571
6572 if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i;
6573 }
6574
6575 TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found");
6576 return -1;
6577}
6578
6579// Search the nearest matching DRM connector mode in connector's list
6580static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
6581{
6582 TRACELOG(LOG_TRACE, "DISPLAY: Searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no");
6583
6584 if (NULL == connector) return -1;
6585
6586 int nearestIndex = -1;
6587 for (int i = 0; i < CORE.Window.connector->count_modes; i++)
6588 {
6589 const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i];
6590
6591 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
6592 (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive");
6593
6594 if ((mode->hdisplay < width) || (mode->vdisplay < height) || (mode->vrefresh < fps))
6595 {
6596 TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small");
6597 continue;
6598 }
6599
6600 if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced))
6601 {
6602 TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode");
6603 continue;
6604 }
6605
6606 if ((mode->hdisplay >= width) && (mode->vdisplay >= height) && (mode->vrefresh >= fps))
6607 {
6608 const int widthDiff = mode->hdisplay - width;
6609 const int heightDiff = mode->vdisplay - height;
6610 const int fpsDiff = mode->vrefresh - fps;
6611
6612 if (nearestIndex < 0)
6613 {
6614 nearestIndex = i;
6615 continue;
6616 }
6617
6618 const int nearestWidthDiff = CORE.Window.connector->modes[nearestIndex].hdisplay - width;
6619 const int nearestHeightDiff = CORE.Window.connector->modes[nearestIndex].vdisplay - height;
6620 const int nearestFpsDiff = CORE.Window.connector->modes[nearestIndex].vrefresh - fps;
6621
6622 if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) nearestIndex = i;
6623 }
6624 }
6625
6626 return nearestIndex;
6627}
6628#endif
6629
6630#if defined(SUPPORT_EVENTS_AUTOMATION)
6631// NOTE: Loading happens over AutomationEvent *events
6632// TODO: This system should probably be redesigned
6633static void LoadAutomationEvents(const char *fileName)
6634{
6635 //unsigned char fileId[4] = { 0 };
6636
6637 // Load binary
6638 /*
6639 FILE *repFile = fopen(fileName, "rb");
6640 fread(fileId, 4, 1, repFile);
6641
6642 if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' '))
6643 {
6644 fread(&eventCount, sizeof(int), 1, repFile);
6645 TraceLog(LOG_WARNING, "Events loaded: %i\n", eventCount);
6646 fread(events, sizeof(AutomationEvent), eventCount, repFile);
6647 }
6648
6649 fclose(repFile);
6650 */
6651
6652 // Load events (text file)
6653 FILE *repFile = fopen(fileName, "rt");
6654
6655 if (repFile != NULL)
6656 {
6657 unsigned int count = 0;
6658 char buffer[256] = { 0 };
6659
6660 fgets(buffer, 256, repFile);
6661
6662 while (!feof(repFile))
6663 {
6664 if (buffer[0] == 'c') sscanf(buffer, "c %i", &eventCount);
6665 else if (buffer[0] == 'e')
6666 {
6667 sscanf(buffer, "e %d %d %d %d %d", &events[count].frame, &events[count].type,
6668 &events[count].params[0], &events[count].params[1], &events[count].params[2]);
6669
6670 count++;
6671 }
6672
6673 fgets(buffer, 256, repFile);
6674 }
6675
6676 if (count != eventCount) TRACELOG(LOG_WARNING, "Events count provided is different than count");
6677
6678 fclose(repFile);
6679 }
6680
6681 TRACELOG(LOG_WARNING, "Events loaded: %i", eventCount);
6682}
6683
6684// Export recorded events into a file
6685static void ExportAutomationEvents(const char *fileName)
6686{
6687 unsigned char fileId[4] = "rEP ";
6688
6689 // Save as binary
6690 /*
6691 FILE *repFile = fopen(fileName, "wb");
6692 fwrite(fileId, 4, 1, repFile);
6693 fwrite(&eventCount, sizeof(int), 1, repFile);
6694 fwrite(events, sizeof(AutomationEvent), eventCount, repFile);
6695 fclose(repFile);
6696 */
6697
6698 // Export events as text
6699 FILE *repFile = fopen(fileName, "wt");
6700
6701 if (repFile != NULL)
6702 {
6703 fprintf(repFile, "# Automation events list\n");
6704 fprintf(repFile, "# c <events_count>\n");
6705 fprintf(repFile, "# e <frame> <event_type> <param0> <param1> <param2> // <event_type_name>\n");
6706
6707 fprintf(repFile, "c %i\n", eventCount);
6708 for (int i = 0; i < eventCount; i++)
6709 {
6710 fprintf(repFile, "e %i %i %i %i %i // %s\n", events[i].frame, events[i].type,
6711 events[i].params[0], events[i].params[1], events[i].params[2], autoEventTypeName[events[i].type]);
6712 }
6713
6714 fclose(repFile);
6715 }
6716}
6717
6718// EndDrawing() -> After PollInputEvents()
6719// Check event in current frame and save into the events[i] array
6720static void RecordAutomationEvent(unsigned int frame)
6721{
6722 for (int key = 0; key < MAX_KEYBOARD_KEYS; key++)
6723 {
6724 // INPUT_KEY_UP (only saved once)
6725 if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key])
6726 {
6727 events[eventCount].frame = frame;
6728 events[eventCount].type = INPUT_KEY_UP;
6729 events[eventCount].params[0] = key;
6730 events[eventCount].params[1] = 0;
6731 events[eventCount].params[2] = 0;
6732
6733 TRACELOG(LOG_INFO, "[%i] INPUT_KEY_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6734 eventCount++;
6735 }
6736
6737 // INPUT_KEY_DOWN
6738 if (CORE.Input.Keyboard.currentKeyState[key])
6739 {
6740 events[eventCount].frame = frame;
6741 events[eventCount].type = INPUT_KEY_DOWN;
6742 events[eventCount].params[0] = key;
6743 events[eventCount].params[1] = 0;
6744 events[eventCount].params[2] = 0;
6745
6746 TRACELOG(LOG_INFO, "[%i] INPUT_KEY_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6747 eventCount++;
6748 }
6749 }
6750
6751 for (int button = 0; button < MAX_MOUSE_BUTTONS; button++)
6752 {
6753 // INPUT_MOUSE_BUTTON_UP
6754 if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button])
6755 {
6756 events[eventCount].frame = frame;
6757 events[eventCount].type = INPUT_MOUSE_BUTTON_UP;
6758 events[eventCount].params[0] = button;
6759 events[eventCount].params[1] = 0;
6760 events[eventCount].params[2] = 0;
6761
6762 TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6763 eventCount++;
6764 }
6765
6766 // INPUT_MOUSE_BUTTON_DOWN
6767 if (CORE.Input.Mouse.currentButtonState[button])
6768 {
6769 events[eventCount].frame = frame;
6770 events[eventCount].type = INPUT_MOUSE_BUTTON_DOWN;
6771 events[eventCount].params[0] = button;
6772 events[eventCount].params[1] = 0;
6773 events[eventCount].params[2] = 0;
6774
6775 TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6776 eventCount++;
6777 }
6778 }
6779
6780 // INPUT_MOUSE_POSITION (only saved if changed)
6781 if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) ||
6782 ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y))
6783 {
6784 events[eventCount].frame = frame;
6785 events[eventCount].type = INPUT_MOUSE_POSITION;
6786 events[eventCount].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
6787 events[eventCount].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
6788 events[eventCount].params[2] = 0;
6789
6790 TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6791 eventCount++;
6792 }
6793
6794 // INPUT_MOUSE_WHEEL_MOTION
6795 if ((int)CORE.Input.Mouse.currentWheelMove != (int)CORE.Input.Mouse.previousWheelMove)
6796 {
6797 events[eventCount].frame = frame;
6798 events[eventCount].type = INPUT_MOUSE_WHEEL_MOTION;
6799 events[eventCount].params[0] = (int)CORE.Input.Mouse.currentWheelMove;
6800 events[eventCount].params[1] = 0;
6801 events[eventCount].params[2] = 0;
6802
6803 TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_WHEEL_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6804 eventCount++;
6805 }
6806
6807 for (int id = 0; id < MAX_TOUCH_POINTS; id++)
6808 {
6809 // INPUT_TOUCH_UP
6810 if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id])
6811 {
6812 events[eventCount].frame = frame;
6813 events[eventCount].type = INPUT_TOUCH_UP;
6814 events[eventCount].params[0] = id;
6815 events[eventCount].params[1] = 0;
6816 events[eventCount].params[2] = 0;
6817
6818 TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6819 eventCount++;
6820 }
6821
6822 // INPUT_TOUCH_DOWN
6823 if (CORE.Input.Touch.currentTouchState[id])
6824 {
6825 events[eventCount].frame = frame;
6826 events[eventCount].type = INPUT_TOUCH_DOWN;
6827 events[eventCount].params[0] = id;
6828 events[eventCount].params[1] = 0;
6829 events[eventCount].params[2] = 0;
6830
6831 TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6832 eventCount++;
6833 }
6834
6835 // INPUT_TOUCH_POSITION
6836 // TODO: It requires the id!
6837 /*
6838 if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) ||
6839 ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y))
6840 {
6841 events[eventCount].frame = frame;
6842 events[eventCount].type = INPUT_TOUCH_POSITION;
6843 events[eventCount].params[0] = id;
6844 events[eventCount].params[1] = (int)CORE.Input.Touch.currentPosition[id].x;
6845 events[eventCount].params[2] = (int)CORE.Input.Touch.currentPosition[id].y;
6846
6847 TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6848 eventCount++;
6849 }
6850 */
6851 }
6852
6853 for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++)
6854 {
6855 // INPUT_GAMEPAD_CONNECT
6856 /*
6857 if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
6858 (CORE.Input.Gamepad.currentState[gamepad] == true)) // Check if changed to ready
6859 {
6860 // TODO: Save gamepad connect event
6861 }
6862 */
6863
6864 // INPUT_GAMEPAD_DISCONNECT
6865 /*
6866 if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
6867 (CORE.Input.Gamepad.currentState[gamepad] == false)) // Check if changed to not-ready
6868 {
6869 // TODO: Save gamepad disconnect event
6870 }
6871 */
6872
6873 for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++)
6874 {
6875 // INPUT_GAMEPAD_BUTTON_UP
6876 if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button])
6877 {
6878 events[eventCount].frame = frame;
6879 events[eventCount].type = INPUT_GAMEPAD_BUTTON_UP;
6880 events[eventCount].params[0] = gamepad;
6881 events[eventCount].params[1] = button;
6882 events[eventCount].params[2] = 0;
6883
6884 TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6885 eventCount++;
6886 }
6887
6888 // INPUT_GAMEPAD_BUTTON_DOWN
6889 if (CORE.Input.Gamepad.currentButtonState[gamepad][button])
6890 {
6891 events[eventCount].frame = frame;
6892 events[eventCount].type = INPUT_GAMEPAD_BUTTON_DOWN;
6893 events[eventCount].params[0] = gamepad;
6894 events[eventCount].params[1] = button;
6895 events[eventCount].params[2] = 0;
6896
6897 TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6898 eventCount++;
6899 }
6900 }
6901
6902 for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
6903 {
6904 // INPUT_GAMEPAD_AXIS_MOTION
6905 if (CORE.Input.Gamepad.axisState[gamepad][axis] > 0.1f)
6906 {
6907 events[eventCount].frame = frame;
6908 events[eventCount].type = INPUT_GAMEPAD_AXIS_MOTION;
6909 events[eventCount].params[0] = gamepad;
6910 events[eventCount].params[1] = axis;
6911 events[eventCount].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
6912
6913 TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_AXIS_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6914 eventCount++;
6915 }
6916 }
6917 }
6918
6919 // INPUT_GESTURE
6920 if (GESTURES.current != GESTURE_NONE)
6921 {
6922 events[eventCount].frame = frame;
6923 events[eventCount].type = INPUT_GESTURE;
6924 events[eventCount].params[0] = GESTURES.current;
6925 events[eventCount].params[1] = 0;
6926 events[eventCount].params[2] = 0;
6927
6928 TRACELOG(LOG_INFO, "[%i] INPUT_GESTURE: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
6929 eventCount++;
6930 }
6931}
6932
6933// Play automation event
6934static void PlayAutomationEvent(unsigned int frame)
6935{
6936 for (unsigned int i = 0; i < eventCount; i++)
6937 {
6938 if (events[i].frame == frame)
6939 {
6940 switch (events[i].type)
6941 {
6942 // Input events
6943 case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = false; break; // param[0]: key
6944 case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = true; break; // param[0]: key
6945 case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = false; break; // param[0]: key
6946 case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = true; break; // param[0]: key
6947 case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y
6948 {
6949 CORE.Input.Mouse.currentPosition.x = (float)events[i].params[0];
6950 CORE.Input.Mouse.currentPosition.y = (float)events[i].params[1];
6951 } break;
6952 case INPUT_MOUSE_WHEEL_MOTION: CORE.Input.Mouse.currentWheelMove = (float)events[i].params[0]; break; // param[0]: delta
6953 case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[events[i].params[0]] = false; break; // param[0]: id
6954 case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[events[i].params[0]] = true; break; // param[0]: id
6955 case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y
6956 {
6957 CORE.Input.Touch.position[events[i].params[0]].x = (float)events[i].params[1];
6958 CORE.Input.Touch.position[events[i].params[0]].y = (float)events[i].params[2];
6959 } break;
6960 case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = true; break; // param[0]: gamepad
6961 case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = false; break; // param[0]: gamepad
6962 case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = false; break; // param[0]: gamepad, param[1]: button
6963 case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = true; break; // param[0]: gamepad, param[1]: button
6964 case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
6965 {
6966 CORE.Input.Gamepad.axisState[events[i].params[0]][events[i].params[1]] = ((float)events[i].params[2]/32768.0f);
6967 } break;
6968 case INPUT_GESTURE: GESTURES.current = events[i].params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
6969
6970 // Window events
6971 case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
6972 case WINDOW_MAXIMIZE: MaximizeWindow(); break;
6973 case WINDOW_MINIMIZE: MinimizeWindow(); break;
6974 case WINDOW_RESIZE: SetWindowSize(events[i].params[0], events[i].params[1]); break;
6975
6976 // Custom events
6977 case ACTION_TAKE_SCREENSHOT:
6978 {
6979 TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
6980 screenshotCounter++;
6981 } break;
6982 case ACTION_SETTARGETFPS: SetTargetFPS(events[i].params[0]); break;
6983 default: break;
6984 }
6985 }
6986 }
6987}
6988#endif
6989
6990#if !defined(SUPPORT_MODULE_RTEXT)
6991// Formatting of text with variables to 'embed'
6992// WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
6993const char *TextFormat(const char *text, ...)
6994{
6995#ifndef MAX_TEXTFORMAT_BUFFERS
6996 #define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting
6997#endif
6998#ifndef MAX_TEXT_BUFFER_LENGTH
6999 #define MAX_TEXT_BUFFER_LENGTH 1024 // Maximum size of static text buffer
7000#endif
7001
7002 // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
7003 static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
7004 static int index = 0;
7005
7006 char *currentBuffer = buffers[index];
7007 memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
7008
7009 va_list args;
7010 va_start(args, text);
7011 vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
7012 va_end(args);
7013
7014 index += 1; // Move to next buffer for next function call
7015 if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
7016
7017 return currentBuffer;
7018}
7019#endif // !SUPPORT_MODULE_RTEXT
void * id
#define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW
Definition: config.h:129
#define MAX_CHAR_PRESSED_QUEUE
Definition: config.h:90
#define MAX_KEYBOARD_KEYS
Definition: config.h:83
#define MAX_MOUSE_BUTTONS
Definition: config.h:84
#define RL_CULL_DISTANCE_NEAR
Definition: config.h:116
#define MAX_FILEPATH_LENGTH
Definition: config.h:80
#define STORAGE_DATA_FILE
Definition: config.h:92
#define MAX_GAMEPAD_BUTTONS
Definition: config.h:87
#define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR
Definition: config.h:133
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT
Definition: config.h:125
#define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL
Definition: config.h:132
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1
Definition: config.h:135
#define MAX_KEY_PRESSED_QUEUE
Definition: config.h:89
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0
Definition: config.h:134
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD
Definition: config.h:122
#define MAX_GAMEPAD_AXIS
Definition: config.h:86
#define MAX_TEXT_BUFFER_LENGTH
Definition: config.h:190
#define RL_CULL_DISTANCE_FAR
Definition: config.h:117
#define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR
Definition: config.h:124
#define RL_MAX_SHADER_LOCATIONS
Definition: config.h:114
#define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL
Definition: config.h:123
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL
Definition: config.h:131
#define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2
Definition: config.h:136
#define MAX_DECOMPRESSION_SIZE
Definition: config.h:94
#define MAX_GAMEPADS
Definition: config.h:85
#define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION
Definition: config.h:121
#define MAX_TOUCH_POINTS
Definition: config.h:88
#define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION
Definition: config.h:130
#define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2
Definition: config.h:126
#define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP
Definition: config.h:128
DIR * opendir(const char *name)
Definition: dirent.h:100
void rewinddir(DIR *dir)
Definition: dirent.h:174
struct dirent * readdir(DIR *dir)
Definition: dirent.h:157
int closedir(DIR *dir)
Definition: dirent.h:139
#define eglCreateContext
Definition: egl_context.h:142
#define eglMakeCurrent
Definition: egl_context.h:146
#define EGL_WINDOW_BIT
Definition: egl_context.h:52
#define EGL_SAMPLES
Definition: egl_context.h:63
#define EGL_NO_CONTEXT
Definition: egl_context.h:74
#define EGL_NATIVE_VISUAL_ID
Definition: egl_context.h:71
void * EGLSurface
Definition: egl_context.h:113
#define eglSwapBuffers
Definition: egl_context.h:147
#define EGL_RENDERABLE_TYPE
Definition: egl_context.h:53
#define EGL_DEFAULT_DISPLAY
Definition: egl_context.h:75
#define EGL_ALPHA_SIZE
Definition: egl_context.h:57
#define EGL_NO_DISPLAY
Definition: egl_context.h:73
#define eglTerminate
Definition: egl_context.h:140
void * EGLContext
Definition: egl_context.h:111
#define eglGetDisplay
Definition: egl_context.h:137
void * EGLNativeDisplayType
Definition: egl_context.h:115
void * EGLConfig
Definition: egl_context.h:110
#define EGL_OPENGL_ES2_BIT
Definition: egl_context.h:55
#define EGL_NONE
Definition: egl_context.h:66
#define EGL_CONTEXT_CLIENT_VERSION
Definition: egl_context.h:70
#define EGL_NO_SURFACE
Definition: egl_context.h:72
#define EGL_GREEN_SIZE
Definition: egl_context.h:59
void * EGLNativeWindowType
Definition: egl_context.h:116
void * EGLDisplay
Definition: egl_context.h:112
#define EGL_OPENGL_ES_API
Definition: egl_context.h:64
#define eglGetProcAddress
Definition: egl_context.h:150
int EGLint
Definition: egl_context.h:107
#define eglGetError
Definition: egl_context.h:138
#define eglDestroyContext
Definition: egl_context.h:144
#define eglBindAPI
Definition: egl_context.h:141
#define eglInitialize
Definition: egl_context.h:139
#define eglCreateWindowSurface
Definition: egl_context.h:145
#define eglDestroySurface
Definition: egl_context.h:143
#define EGL_SURFACE_TYPE
Definition: egl_context.h:51
#define EGL_DEPTH_SIZE
Definition: egl_context.h:61
#define eglGetConfigAttrib
Definition: egl_context.h:135
#define EGL_RED_SIZE
Definition: egl_context.h:60
#define EGL_BLUE_SIZE
Definition: egl_context.h:58
#define glGetString
Definition: glad.h:4435
#define GL_RENDERER
Definition: glad.h:1358
GLFW 3.4 - www.glfw.org.
#define GLFW_EGL_CONTEXT_API
Definition: glfw3.h:1113
#define GLFW_NATIVE_CONTEXT_API
Definition: glfw3.h:1112
#define GLFW_CURSOR_DISABLED
Definition: glfw3.h:1106
#define GLFW_OPENGL_ES_API
Definition: glfw3.h:1088
#define GLFW_DONT_CARE
Definition: glfw3.h:1262
#define GLFW_CURSOR
Definition: glfw3.h:1098
#define GLFW_CURSOR_HIDDEN
Definition: glfw3.h:1105
#define GLFW_CURSOR_NORMAL
Definition: glfw3.h:1104
#define GLFW_OPENGL_CORE_PROFILE
Definition: glfw3.h:1095
GLFW 3.4 - www.glfw.org.
GLFWAPI GLFWglproc glfwGetProcAddress(const char *procname)
Returns the address of the specified function for the current context.
Definition: context.c:739
GLFWAPI void glfwSwapInterval(int interval)
Sets the swap interval for the current context.
Definition: context.c:656
GLFWAPI void glfwMakeContextCurrent(GLFWwindow *window)
Makes the context of the specified window current for the calling thread.
Definition: context.c:609
#define GLFW_GAMEPAD_AXIS_LAST
Definition: glfw3.h:652
#define GLFW_GAMEPAD_BUTTON_START
Definition: glfw3.h:623
#define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER
Definition: glfw3.h:620
#define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB
Definition: glfw3.h:626
#define GLFW_GAMEPAD_BUTTON_B
Definition: glfw3.h:617
#define GLFW_GAMEPAD_BUTTON_LEFT_THUMB
Definition: glfw3.h:625
#define GLFW_GAMEPAD_BUTTON_DPAD_UP
Definition: glfw3.h:627
#define GLFW_GAMEPAD_BUTTON_X
Definition: glfw3.h:618
#define GLFW_GAMEPAD_BUTTON_GUIDE
Definition: glfw3.h:624
#define GLFW_GAMEPAD_BUTTON_DPAD_DOWN
Definition: glfw3.h:629
#define GLFW_GAMEPAD_BUTTON_BACK
Definition: glfw3.h:622
#define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER
Definition: glfw3.h:621
#define GLFW_GAMEPAD_BUTTON_A
Definition: glfw3.h:616
#define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT
Definition: glfw3.h:628
#define GLFW_GAMEPAD_BUTTON_DPAD_LEFT
Definition: glfw3.h:630
#define GLFW_GAMEPAD_BUTTON_Y
Definition: glfw3.h:619
#define GLFW_TRUE
One.
Definition: glfw3.h:310
GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback)
Sets the error callback.
Definition: init.c:352
GLFWAPI int glfwInit(void)
GLFW API functions.
Definition: init.c:235
#define GLFW_COCOA_CHDIR_RESOURCES
macOS specific init hint.
Definition: glfw3.h:1249
GLFWAPI void glfwInitHint(int hint, int value)
Sets the specified init hint to the desired value.
Definition: init.c:288
#define GLFW_FALSE
Zero.
Definition: glfw3.h:319
GLFWAPI void glfwTerminate(void)
Terminates the GLFW library.
Definition: init.c:280
GLFWAPI double glfwGetTime(void)
Returns the GLFW time.
Definition: input.c:1375
#define GLFW_PRESS
The key or mouse button was pressed.
Definition: glfw3.h:336
GLFWAPI int glfwUpdateGamepadMappings(const char *string)
Adds the specified SDL_GameControllerDB gamepad mappings.
Definition: input.c:1156
GLFWAPI const char * glfwGetClipboardString(GLFWwindow *window)
Returns the contents of the clipboard as a string.
Definition: input.c:1369
GLFWAPI const char * glfwGetJoystickName(int jid)
Returns the name of the specified joystick.
Definition: input.c:1057
GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate *state)
Retrieves the state of the specified joystick remapped as a gamepad.
Definition: input.c:1275
GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow *window, GLFWcursorposfun callback)
Sets the cursor position callback.
Definition: input.c:889
GLFWAPI void glfwSetClipboardString(GLFWwindow *window, const char *string)
Sets the clipboard to the specified string.
Definition: input.c:1361
GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow *window, GLFWdropfun callback)
Sets the path drop callback.
Definition: input.c:922
GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow *window, GLFWkeyfun callback)
Sets the key callback.
Definition: input.c:848
GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow *window, GLFWscrollfun callback)
Sets the scroll callback.
Definition: input.c:911
GLFWAPI void glfwSetCursorPos(GLFWwindow *window, double xpos, double ypos)
Sets the position of the cursor, relative to the content area of the window.
Definition: input.c:713
GLFWAPI GLFWcursor * glfwCreateStandardCursor(int shape)
Creates a cursor with a standard shape.
Definition: input.c:766
GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow *window, GLFWmousebuttonfun callback)
Sets the mouse button callback.
Definition: input.c:878
GLFWAPI int glfwJoystickPresent(int jid)
Returns whether the specified joystick is present.
Definition: input.c:932
GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow *window, GLFWcursorenterfun callback)
Sets the cursor enter/leave callback.
Definition: input.c:900
GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow *window, GLFWcharfun callback)
Sets the Unicode character callback.
Definition: input.c:858
GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback)
Sets the joystick configuration callback.
Definition: input.c:1145
#define GLFW_RELEASE
The key or mouse button was released.
Definition: glfw3.h:329
GLFWAPI void glfwSetInputMode(GLFWwindow *window, int mode, int value)
Sets an input option for the specified window.
Definition: input.c:513
GLFWAPI void glfwSetCursor(GLFWwindow *window, GLFWcursor *cursor)
Sets the cursor for the window.
Definition: input.c:835
#define GLFW_KEY_F11
Definition: glfw3.h:473
#define GLFW_KEY_F9
Definition: glfw3.h:471
#define GLFW_KEY_F12
Definition: glfw3.h:474
#define GLFW_MOD_CONTROL
If this bit is set one or more Control keys were held down.
Definition: glfw3.h:536
GLFWAPI const GLFWvidmode * glfwGetVideoMode(GLFWmonitor *monitor)
Returns the current mode of the specified monitor.
Definition: monitor.c:442
GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor *monitor, float *xscale, float *yscale)
Retrieves the content scale for the specified monitor.
Definition: monitor.c:376
GLFWAPI GLFWmonitor * glfwGetPrimaryMonitor(void)
Returns the primary monitor.
Definition: monitor.c:312
GLFWAPI void glfwGetMonitorPos(GLFWmonitor *monitor, int *xpos, int *ypos)
Returns the position of the monitor's viewport on the virtual screen.
Definition: monitor.c:322
GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor *monitor, int *xpos, int *ypos, int *width, int *height)
Retrieves the work area of the monitor.
Definition: monitor.c:337
GLFWAPI const char * glfwGetMonitorName(GLFWmonitor *monitor)
Returns the name of the specified monitor.
Definition: monitor.c:391
GLFWAPI const GLFWvidmode * glfwGetVideoModes(GLFWmonitor *monitor, int *count)
Returns the available video modes for the specified monitor.
Definition: monitor.c:425
GLFWAPI GLFWmonitor ** glfwGetMonitors(int *count)
Returns the currently connected monitors.
Definition: monitor.c:300
struct GLFWmonitor GLFWmonitor
Opaque monitor object.
Definition: glfw3.h:1307
GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor *monitor, int *widthMM, int *heightMM)
Returns the physical size of the monitor.
Definition: monitor.c:358
GLFWAPI void glfwGetWindowPos(GLFWwindow *window, int *xpos, int *ypos)
Retrieves the position of the content area of the specified window.
Definition: window.c:526
GLFWAPI void glfwSetWindowPos(GLFWwindow *window, int xpos, int ypos)
Sets the position of the content area of the specified window.
Definition: window.c:540
GLFWAPI void glfwSetWindowMonitor(GLFWwindow *window, GLFWmonitor *monitor, int xpos, int ypos, int width, int height, int refreshRate)
Sets the mode, monitor, video mode and placement of a window.
Definition: window.c:925
#define GLFW_OPENGL_FORWARD_COMPAT
OpenGL forward-compatibility hint and attribute.
Definition: glfw3.h:1023
GLFWAPI void glfwSetWindowIcon(GLFWwindow *window, int count, const GLFWimage *images)
Sets the icon for the specified window.
Definition: window.c:514
GLFWAPI int glfwGetWindowAttrib(GLFWwindow *window, int attrib)
Returns an attribute of the specified window.
Definition: window.c:813
GLFWAPI void glfwSetWindowShouldClose(GLFWwindow *window, int value)
Sets the close flag of the specified window.
Definition: window.c:495
GLFWAPI void glfwRestoreWindow(GLFWwindow *window)
Restores the specified window.
Definition: window.c:742
#define GLFW_DECORATED
Window decoration window hint and attribute.
Definition: glfw3.h:856
GLFWAPI void glfwIconifyWindow(GLFWwindow *window)
Iconifies the specified window.
Definition: window.c:733
#define GLFW_SAMPLES
Framebuffer MSAA samples hint.
Definition: glfw3.h:970
#define GLFW_CONTEXT_VERSION_MINOR
Context client API minor version hint and attribute.
Definition: glfw3.h:1005
struct GLFWwindow GLFWwindow
Opaque window object.
Definition: glfw3.h:1319
GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow *window, GLFWwindowiconifyfun callback)
Sets the iconify callback for the specified window.
Definition: window.c:1037
#define GLFW_OPENGL_PROFILE
OpenGL profile hint and attribute.
Definition: glfw3.h:1040
GLFWAPI void glfwMaximizeWindow(GLFWwindow *window)
Maximizes the specified window.
Definition: window.c:751
#define GLFW_CONTEXT_CREATION_API
Context creation API hint and attribute.
Definition: glfw3.h:1058
#define GLFW_FOCUSED
Input focus window hint and attribute.
Definition: glfw3.h:833
#define GLFW_TRANSPARENT_FRAMEBUFFER
Window framebuffer transparency hint and attribute.
Definition: glfw3.h:886
#define GLFW_SCALE_TO_MONITOR
Window content area scaling window window hint.
Definition: glfw3.h:1062
GLFWAPI void glfwSetWindowOpacity(GLFWwindow *window, float opacity)
Sets the opacity of the whole window.
Definition: window.c:714
#define GLFW_CLIENT_API
Context client API hint and attribute.
Definition: glfw3.h:993
GLFWAPI void glfwWindowHint(int hint, int value)
Sets the specified window hint to the desired value.
Definition: window.c:294
GLFWAPI void glfwWaitEvents(void)
Waits until events are queued and processes them.
Definition: window.c:1087
GLFWAPI void glfwGetWindowContentScale(GLFWwindow *window, float *xscale, float *yscale)
Retrieves the content scale for the specified window.
Definition: window.c:690
GLFWAPI void glfwShowWindow(GLFWwindow *window)
Makes the specified window visible.
Definition: window.c:764
#define GLFW_FLOATING
Window decoration window hint and attribute.
Definition: glfw3.h:868
GLFWAPI void glfwDefaultWindowHints(void)
Resets all window hints to their default values.
Definition: window.c:255
GLFWAPI void glfwDestroyWindow(GLFWwindow *window)
Destroys the specified window and its context.
Definition: window.c:453
GLFWAPI void glfwSetWindowTitle(GLFWwindow *window, const char *title)
Sets the title of the specified window.
Definition: window.c:504
GLFWAPI void glfwPollEvents(void)
Processes all pending events.
Definition: window.c:1081
#define GLFW_OPENGL_DEBUG_CONTEXT
Legacy name for compatibility.
Definition: glfw3.h:1034
GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow *window, int minwidth, int minheight, int maxwidth, int maxheight)
Sets the size limits of the specified window.
Definition: window.c:582
#define GLFW_AUTO_ICONIFY
Window auto-iconification window hint and attribute.
Definition: glfw3.h:862
GLFWAPI void glfwHideWindow(GLFWwindow *window)
Hides the specified window.
Definition: window.c:790
GLFWAPI int glfwWindowShouldClose(GLFWwindow *window)
Checks the close flag of the specified window.
Definition: window.c:486
GLFWAPI GLFWwindow * glfwCreateWindow(int width, int height, const char *title, GLFWmonitor *monitor, GLFWwindow *share)
Creates a window and its associated context.
Definition: window.c:152
GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow *window, GLFWwindowsizefun callback)
Sets the size callback for the specified window.
Definition: window.c:993
#define GLFW_COCOA_RETINA_FRAMEBUFFER
macOS specific window hint.
Definition: glfw3.h:1066
GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow *window, GLFWwindowfocusfun callback)
Sets the focus callback for the specified window.
Definition: window.c:1026
GLFWAPI void glfwSetWindowAttrib(GLFWwindow *window, int attrib, int value)
Sets an attribute of the specified window.
Definition: window.c:876
GLFWAPI GLFWmonitor * glfwGetWindowMonitor(GLFWwindow *window)
Returns the monitor that the window uses for full screen mode.
Definition: window.c:916
#define GLFW_RESIZABLE
Window resize-ability window hint and attribute.
Definition: glfw3.h:844
GLFWAPI void glfwSetWindowSize(GLFWwindow *window, int width, int height)
Sets the size of the content area of the specified window.
Definition: window.c:567
GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow *window, GLFWwindowmaximizefun callback)
Sets the maximize callback for the specified window.
Definition: window.c:1048
GLFWAPI void glfwGetFramebufferSize(GLFWwindow *window, int *width, int *height)
Retrieves the size of the framebuffer of the specified window.
Definition: window.c:656
#define GLFW_FOCUS_ON_SHOW
Input focus on calling show window hint and attribute.
Definition: glfw3.h:897
#define GLFW_VISIBLE
Window visibility window hint and attribute.
Definition: glfw3.h:850
GLFWAPI void glfwSwapBuffers(GLFWwindow *window)
Swaps the front and back buffers of the specified window.
Definition: context.c:639
#define GLFW_CONTEXT_VERSION_MAJOR
Context client API major version hint and attribute.
Definition: glfw3.h:999
int main()
Definition: main.cpp:4
#define NULL
Definition: miniaudio.h:3718
void msf_gif_free(MsfGifResult result)
int msf_gif_frame(MsfGifState *handle, uint8_t *pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes)
MsfGifResult msf_gif_end(MsfGifState *handle)
int msf_gif_begin(MsfGifState *handle, int width, int height)
#define RL_FREE(p)
Definition: raudio.h:67
#define RL_MALLOC(sz)
raudio v1.0 - A simple and easy-to-use audio library based on miniaudio
Definition: raudio.h:61
#define RL_CALLOC(n, sz)
Definition: raudio.h:64
#define TRACELOG(level,...)
Definition: raygui.h:222
@ SHADER_LOC_MATRIX_MODEL
Definition: raylib.h:733
@ SHADER_LOC_COLOR_DIFFUSE
Definition: raylib.h:736
@ SHADER_LOC_MATRIX_MVP
Definition: raylib.h:730
@ SHADER_LOC_VERTEX_COLOR
Definition: raylib.h:729
@ SHADER_LOC_VERTEX_TANGENT
Definition: raylib.h:728
@ SHADER_LOC_MATRIX_PROJECTION
Definition: raylib.h:732
@ SHADER_LOC_VERTEX_TEXCOORD01
Definition: raylib.h:725
@ SHADER_LOC_MATRIX_VIEW
Definition: raylib.h:731
@ SHADER_LOC_MAP_NORMAL
Definition: raylib.h:741
@ SHADER_LOC_VERTEX_POSITION
Definition: raylib.h:724
@ SHADER_LOC_VERTEX_TEXCOORD02
Definition: raylib.h:726
@ SHADER_LOC_MATRIX_NORMAL
Definition: raylib.h:734
@ SHADER_LOC_VERTEX_NORMAL
Definition: raylib.h:727
RLAPI const char * TextFormat(const char *text,...)
Definition: rtext.c:1278
@ MOUSE_CURSOR_DEFAULT
Definition: raylib.h:659
@ MOUSE_CURSOR_ARROW
Definition: raylib.h:660
#define RAYWHITE
Definition: raylib.h:174
RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite)
Definition: utils.c:237
@ CAMERA_PERSPECTIVE
Definition: raylib.h:877
@ CAMERA_ORTHOGRAPHIC
Definition: raylib.h:878
RLAPI void UnloadFileData(unsigned char *data)
Definition: utils.c:231
@ GAMEPAD_AXIS_RIGHT_TRIGGER
Definition: raylib.h:701
@ GAMEPAD_AXIS_LEFT_TRIGGER
Definition: raylib.h:700
@ BLEND_ALPHA
Definition: raylib.h:841
#define LIME
Definition: raylib.h:158
RLAPI void SetShapesTexture(Texture2D texture, Rectangle source)
Definition: rshapes.c:98
@ GESTURE_NONE
Definition: raylib.h:853
@ LOG_ERROR
Definition: raylib.h:516
@ LOG_TRACE
Definition: raylib.h:512
@ LOG_INFO
Definition: raylib.h:514
@ LOG_WARNING
Definition: raylib.h:515
@ LOG_FATAL
Definition: raylib.h:517
@ PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
Definition: raylib.h:785
RLAPI unsigned char * LoadFileData(const char *fileName, unsigned int *bytesRead)
Definition: utils.c:182
#define RL_REALLOC(ptr, sz)
Definition: raylib.h:120
RLAPI const char * TextToLower(const char *text)
Definition: rtext.c:1573
GamepadButton
Definition: raylib.h:673
@ GAMEPAD_BUTTON_LEFT_FACE_DOWN
Definition: raylib.h:677
@ GAMEPAD_BUTTON_LEFT_FACE_UP
Definition: raylib.h:675
@ GAMEPAD_BUTTON_RIGHT_THUMB
Definition: raylib.h:691
@ GAMEPAD_BUTTON_LEFT_TRIGGER_1
Definition: raylib.h:683
@ GAMEPAD_BUTTON_LEFT_FACE_LEFT
Definition: raylib.h:678
@ GAMEPAD_BUTTON_MIDDLE_LEFT
Definition: raylib.h:687
@ GAMEPAD_BUTTON_LEFT_TRIGGER_2
Definition: raylib.h:684
@ GAMEPAD_BUTTON_RIGHT_FACE_DOWN
Definition: raylib.h:681
@ GAMEPAD_BUTTON_RIGHT_FACE_LEFT
Definition: raylib.h:682
@ GAMEPAD_BUTTON_LEFT_FACE_RIGHT
Definition: raylib.h:676
@ GAMEPAD_BUTTON_RIGHT_FACE_RIGHT
Definition: raylib.h:680
@ GAMEPAD_BUTTON_LEFT_THUMB
Definition: raylib.h:690
@ GAMEPAD_BUTTON_MIDDLE_RIGHT
Definition: raylib.h:689
@ GAMEPAD_BUTTON_RIGHT_FACE_UP
Definition: raylib.h:679
@ GAMEPAD_BUTTON_RIGHT_TRIGGER_2
Definition: raylib.h:686
@ GAMEPAD_BUTTON_MIDDLE
Definition: raylib.h:688
@ GAMEPAD_BUTTON_RIGHT_TRIGGER_1
Definition: raylib.h:685
RLAPI void TraceLog(int logLevel, const char *text,...)
Definition: utils.c:107
RLAPI Font GetFontDefault(void)
Definition: rtext.c:289
RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color)
Definition: rtext.c:1007
#define SHADER_LOC_MAP_SPECULAR
Definition: raylib.h:753
RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color)
Definition: rshapes.c:664
#define RED
Definition: raylib.h:155
@ KEY_SPACE
Definition: raylib.h:575
@ KEY_ESCAPE
Definition: raylib.h:576
#define RLAPI
Definition: raylib.h:96
RLAPI bool ExportImage(Image image, const char *fileName)
Definition: rtextures.c:464
#define RAYLIB_VERSION
raylib v4.1-dev - A simple and easy-to-use library to enjoy videogames programming (www....
Definition: raylib.h:83
@ MOUSE_BUTTON_FORWARD
Definition: raylib.h:653
@ MOUSE_BUTTON_SIDE
Definition: raylib.h:651
@ MOUSE_BUTTON_LEFT
Definition: raylib.h:648
@ MOUSE_BUTTON_RIGHT
Definition: raylib.h:649
@ MOUSE_BUTTON_MIDDLE
Definition: raylib.h:650
@ MOUSE_BUTTON_EXTRA
Definition: raylib.h:652
@ MOUSE_BUTTON_BACK
Definition: raylib.h:654
@ FLAG_WINDOW_TOPMOST
Definition: raylib.h:500
@ FLAG_INTERLACED_HINT
Definition: raylib.h:505
@ FLAG_VSYNC_HINT
Definition: raylib.h:492
@ FLAG_WINDOW_HIGHDPI
Definition: raylib.h:503
@ FLAG_WINDOW_UNFOCUSED
Definition: raylib.h:499
@ FLAG_WINDOW_HIDDEN
Definition: raylib.h:496
@ FLAG_WINDOW_ALWAYS_RUN
Definition: raylib.h:501
@ FLAG_WINDOW_MAXIMIZED
Definition: raylib.h:498
@ FLAG_MSAA_4X_HINT
Definition: raylib.h:504
@ FLAG_WINDOW_MINIMIZED
Definition: raylib.h:497
@ FLAG_WINDOW_UNDECORATED
Definition: raylib.h:495
@ FLAG_WINDOW_RESIZABLE
Definition: raylib.h:494
@ FLAG_FULLSCREEN_MODE
Definition: raylib.h:493
@ FLAG_WINDOW_TRANSPARENT
Definition: raylib.h:502
RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color)
Definition: rshapes.c:256
RLAPI void UnloadFileText(char *text)
Definition: utils.c:370
#define MAROON
Definition: raylib.h:156
#define GREEN
Definition: raylib.h:157
RLAPI const char ** TextSplit(const char *text, char delimiter, int *count)
Definition: rtext.c:1488
RLAPI char * LoadFileText(const char *fileName)
Definition: utils.c:318
#define DEG2RAD
Definition: raylib.h:106
#define SHADER_LOC_MAP_DIFFUSE
Definition: raylib.h:752
RMAPI Vector3 Vector3Normalize(Vector3 v)
Definition: raymath.h:670
RMAPI Vector3 Vector3Transform(Vector3 v, Matrix mat)
Definition: raymath.h:721
#define MatrixToFloat(mat)
Definition: raymath.h:94
RMAPI Matrix MatrixRotate(Vector3 axis, float angle)
Definition: raymath.h:1213
RMAPI Matrix MatrixScale(float x, float y, float z)
Definition: raymath.h:1380
RMAPI Matrix MatrixIdentity(void)
Definition: raymath.h:1114
RMAPI Vector3 Vector3Subtract(Vector3 v1, Vector3 v2)
Definition: raymath.h:522
RMAPI Matrix MatrixTranslate(float x, float y, float z)
Definition: raymath.h:1201
RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view)
Definition: raymath.h:828
RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat)
Definition: raymath.h:1989
RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double far)
Definition: raymath.h:1424
RMAPI Matrix MatrixInvert(Matrix mat)
Definition: raymath.h:1067
RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far)
Definition: raymath.h:1450
RMAPI Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up)
Definition: raymath.h:1479
RMAPI Matrix MatrixMultiply(Matrix left, Matrix right)
Definition: raymath.h:1176
Vector2 GetWorldToScreen(Vector3 position, Camera camera)
Definition: rcore.c:2605
void BeginScissorMode(int x, int y, int width, int height)
Definition: rcore.c:2269
bool IsGamepadButtonReleased(int gamepad, int button)
Definition: rcore.c:3656
int GetFPS(void)
Definition: rcore.c:2685
bool IsFileDropped(void)
Definition: rcore.c:3151
const char * GetFileName(const char *filePath)
Definition: rcore.c:2900
void SetMousePosition(int x, int y)
Definition: rcore.c:3787
void BeginVrStereoMode(VrStereoConfig config)
Definition: rcore.c:2299
int GetCharPressed(void)
Definition: rcore.c:3551
unsigned char * CompressData(const unsigned char *data, int dataSize, int *compDataSize)
Definition: rcore.c:3193
bool IsGamepadButtonUp(int gamepad, int button)
Definition: rcore.c:3667
const char * GetDirectoryPath(const char *filePath)
Definition: rcore.c:2936
bool IsKeyDown(int key)
Definition: rcore.c:3505
bool IsWindowFullscreen(void)
Definition: rcore.c:1115
void MaximizeWindow(void)
Definition: rcore.c:1295
void BeginTextureMode(RenderTexture2D target)
Definition: rcore.c:2198
void UnloadFontDefault(void)
Definition: rtext.c:279
#define GETCWD
Definition: rcore.c:204
bool IsMouseButtonPressed(int button)
Definition: rcore.c:3696
#define FPS_STEP
void SetConfigFlags(unsigned int flags)
Definition: rcore.c:2744
Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height)
Definition: rcore.c:2613
void ClearDirectoryFiles(void)
Definition: rcore.c:3128
void InitWindow(int width, int height, const char *title)
Definition: rcore.c:723
void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
Definition: rcore.c:2508
const char * GetWorkingDirectory(void)
Definition: rcore.c:3006
void SetClipboardText(const char *text)
Definition: rcore.c:1916
void SetWindowOpacity(float opacity)
Definition: rcore.c:1607
int GetScreenWidth(void)
Definition: rcore.c:1617
char ** GetDroppedFiles(int *count)
Definition: rcore.c:3158
void BeginDrawing(void)
Definition: rcore.c:1992
int GetMouseX(void)
Definition: rcore.c:3741
const char * GetPrevDirectoryPath(const char *dirPath)
Definition: rcore.c:2982
void SetWindowMinSize(int width, int height)
Definition: rcore.c:1590
bool WindowShouldClose(void)
Definition: rcore.c:1074
void EndDrawing(void)
Definition: rcore.c:2009
void BeginMode3D(Camera3D camera)
Definition: rcore.c:2143
bool DirectoryExists(const char *dirPath)
Definition: rcore.c:2849
bool IsKeyPressed(int key)
Definition: rcore.c:3495
bool IsWindowResized(void)
Definition: rcore.c:1160
int GetRenderWidth(void)
Definition: rcore.c:1629
void SetWindowPosition(int x, int y)
Definition: rcore.c:1564
bool IsCursorHidden(void)
Definition: rcore.c:1947
void CloseWindow(void)
Definition: rcore.c:926
char ** GetDirectoryFiles(const char *dirPath, int *fileCount)
Definition: rcore.c:3094
int GetTouchY(void)
Definition: rcore.c:3849
void BeginShaderMode(Shader shader)
Definition: rcore.c:2243
int GetRenderHeight(void)
Definition: rcore.c:1635
void EnableCursor(void)
Definition: rcore.c:1953
void SwapScreenBuffer(void)
Definition: rcore.c:4850
Matrix GetCameraMatrix(Camera camera)
Definition: rcore.c:2571
bool IsGamepadButtonPressed(int gamepad, int button)
Definition: rcore.c:3634
void SetRandomSeed(unsigned int seed)
Definition: rcore.c:2792
bool IsFileExtension(const char *fileName, const char *ext)
Definition: rcore.c:2818
void UnloadVrStereoConfig(VrStereoConfig config)
Definition: rcore.c:2389
const char * GetClipboardText(void)
Definition: rcore.c:1904
int GetTouchPointId(int index)
Definition: rcore.c:3894
bool IsKeyUp(int key)
Definition: rcore.c:3522
Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
Definition: rcore.c:2666
bool SaveStorageValue(unsigned int position, int value)
Definition: rcore.c:3330
void ToggleFullscreen(void)
Definition: rcore.c:1176
Vector2 GetTouchPosition(int index)
Definition: rcore.c:3860
#define FPS_CAPTURE_FRAMES_COUNT
bool IsMouseButtonUp(int button)
Definition: rcore.c:3735
void * GetWindowHandle(void)
Definition: rcore.c:1641
bool IsWindowMaximized(void)
Definition: rcore.c:1140
void MinimizeWindow(void)
Definition: rcore.c:1307
float GetGamepadAxisMovement(int gamepad, int axis)
Definition: rcore.c:3623
int GetRandomValue(int min, int max)
Definition: rcore.c:2779
void SetMouseCursor(int cursor)
Definition: rcore.c:3825
const char * GetApplicationDirectory(void)
Definition: rcore.c:3016
const char * GetFileNameWithoutExt(const char *filePath)
Definition: rcore.c:2911
bool IsGamepadButtonDown(int gamepad, int button)
Definition: rcore.c:3645
void EndVrStereoMode(void)
Definition: rcore.c:2309
int GetKeyPressed(void)
Definition: rcore.c:3529
#define MAX_FILENAMEWITHOUTEXT_LENGTH
RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
Definition: rcore.c:2415
void SetTargetFPS(int fps)
Definition: rcore.c:2675
int GetMonitorWidth(int monitor)
Definition: rcore.c:1742
bool IsGamepadAvailable(int gamepad)
Definition: rcore.c:3584
void EndScissorMode(void)
Definition: rcore.c:2292
int GetGamepadButtonPressed(void)
Definition: rcore.c:3678
void EndTextureMode(void)
Definition: rcore.c:2228
int GetMonitorHeight(int monitor)
Definition: rcore.c:1763
void SetMouseScale(float scaleX, float scaleY)
Definition: rcore.c:3805
void ClearWindowState(unsigned int flags)
Definition: rcore.c:1433
void LoadFontDefault(void)
Definition: rtext.c:130
Vector2 GetWindowScaleDPI(void)
Definition: rcore.c:1853
double GetTime(void)
Definition: rcore.c:2725
bool ChangeDirectory(const char *dir)
Definition: rcore.c:3141
bool IsCursorOnScreen(void)
Definition: rcore.c:1979
const char * GetMonitorName(int monitor)
Definition: rcore.c:1887
Vector2 GetMonitorPosition(int monitor)
Definition: rcore.c:1723
bool IsWindowReady(void)
Definition: rcore.c:1109
void EndMode2D(void)
Definition: rcore.c:2134
void PollInputEvents(void)
Definition: rcore.c:4890
void RestoreWindow(void)
Definition: rcore.c:1316
bool FileExists(const char *fileName)
Definition: rcore.c:2798
void SetMouseOffset(int offsetX, int offsetY)
Definition: rcore.c:3798
const char * GetFileExtension(const char *fileName)
Definition: rcore.c:2882
float GetMouseWheelMove(void)
Definition: rcore.c:3811
int LoadStorageValue(unsigned int position)
Definition: rcore.c:3404
void HideCursor(void)
Definition: rcore.c:1937
int GetFileLength(const char *fileName)
Definition: rcore.c:2865
Shader LoadShader(const char *vsFileName, const char *fsFileName)
Definition: rcore.c:2396
void ClearBackground(Color color)
Definition: rcore.c:1985
void EndBlendMode(void)
Definition: rcore.c:2262
int GetMonitorPhysicalHeight(int monitor)
Definition: rcore.c:1802
#define COMPRESSION_QUALITY_DEFLATE
void SetWindowMonitor(int monitor)
Definition: rcore.c:1572
void BeginMode2D(Camera2D camera)
Definition: rcore.c:2120
const char * GetGamepadName(int gamepad)
Definition: rcore.c:3594
int GetMouseY(void)
Definition: rcore.c:3751
const char * raylibVersion
Definition: rcore.c:508
#define CHDIR
Definition: rcore.c:205
int GetCurrentMonitor(void)
Definition: rcore.c:1675
void UnloadShader(Shader shader)
Definition: rcore.c:2464
bool IsWindowFocused(void)
Definition: rcore.c:1150
void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
Definition: rcore.c:2492
bool IsMouseButtonDown(int button)
Definition: rcore.c:3709
void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType)
Definition: rcore.c:2486
void EndMode3D(void)
Definition: rcore.c:2182
void ClearDroppedFiles(void)
Definition: rcore.c:3165
void SetExitKey(int key)
Definition: rcore.c:3574
int GetMonitorPhysicalWidth(int monitor)
Definition: rcore.c:1784
bool IsWindowState(unsigned int flag)
Definition: rcore.c:1170
int GetMonitorCount(void)
Definition: rcore.c:1663
VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
Definition: rcore.c:2315
int GetScreenHeight(void)
Definition: rcore.c:1623
void SetWindowSize(int width, int height)
Definition: rcore.c:1599
unsigned char * DecodeDataBase64(const unsigned char *data, int *outputSize)
Definition: rcore.c:3271
int SetGamepadMappings(const char *mappings)
Definition: rcore.c:3684
bool IsWindowMinimized(void)
Definition: rcore.c:1130
void SetWindowIcon(Image image)
Definition: rcore.c:1535
int GetShaderLocationAttrib(Shader shader, const char *attribName)
Definition: rcore.c:2480
bool IsMouseButtonReleased(int button)
Definition: rcore.c:3722
bool IsWindowHidden(void)
Definition: rcore.c:1121
int GetTouchX(void)
Definition: rcore.c:3839
Vector2 GetMousePosition(void)
Definition: rcore.c:3761
#define GIF_RECORD_FRAMERATE
void WaitTime(float ms)
Definition: rcore.c:4807
void ShowCursor(void)
Definition: rcore.c:1927
Matrix GetCameraMatrix2D(Camera2D camera)
Definition: rcore.c:2577
long GetFileModTime(const char *fileName)
Definition: rcore.c:3178
Ray GetMouseRay(Vector2 mouse, Camera camera)
Definition: rcore.c:2516
void SetWindowTitle(const char *title)
Definition: rcore.c:1555
void BeginBlendMode(int mode)
Definition: rcore.c:2256
void OpenURL(const char *url)
Definition: rcore.c:3437
char * EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
Definition: rcore.c:3235
void TakeScreenshot(const char *fileName)
Definition: rcore.c:2754
void DisableCursor(void)
Definition: rcore.c:1966
int GetMonitorRefreshRate(int monitor)
Definition: rcore.c:1819
void EndShaderMode(void)
Definition: rcore.c:2249
Vector2 GetMouseDelta(void)
Definition: rcore.c:3776
void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
Definition: rcore.c:2500
float GetFrameTime(void)
Definition: rcore.c:2717
int GetGamepadAxisCount(int gamepad)
Definition: rcore.c:3611
int GetTouchPointCount(void)
Definition: rcore.c:3904
void SetWindowState(unsigned int flags)
Definition: rcore.c:1330
bool IsKeyReleased(int key)
Definition: rcore.c:3512
unsigned char * DecompressData(const unsigned char *compData, int compDataSize, int *dataSize)
Definition: rcore.c:3213
Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera)
Definition: rcore.c:2657
int GetShaderLocation(Shader shader, const char *uniformName)
Definition: rcore.c:2474
Vector2 GetWindowPosition(void)
Definition: rcore.c:1842
void ProcessGestureEvent(GestureEvent event)
void UpdateGestures(void)
@ TOUCH_ACTION_CANCEL
Definition: rgestures.h:101
@ TOUCH_ACTION_DOWN
Definition: rgestures.h:99
@ TOUCH_ACTION_MOVE
Definition: rgestures.h:100
@ TOUCH_ACTION_UP
Definition: rgestures.h:98
RLAPI int * rlGetShaderLocsDefault(void)
RLAPI void rlClearScreenBuffers(void)
RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count)
RLAPI void rlSetBlendMode(int mode)
RLAPI void rlLoadIdentity(void)
RLAPI void rlglClose(void)
RLAPI void rlDisableScissorTest(void)
#define RL_MODELVIEW
Definition: rlgl.h:253
RLAPI void rlEnableStereoRender(void)
@ OPENGL_43
Definition: rlgl.h:289
@ OPENGL_21
Definition: rlgl.h:287
@ OPENGL_ES_20
Definition: rlgl.h:290
@ OPENGL_33
Definition: rlgl.h:288
RLAPI void rlOrtho(double left, double right, double bottom, double top, double znear, double zfar)
RLAPI void rlLoadExtensions(void *loader)
RLAPI unsigned int rlGetShaderIdDefault(void)
RLAPI unsigned char * rlReadScreenPixels(int width, int height)
RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat)
RLAPI void rlSetFramebufferHeight(int height)
RLAPI void rlDisableStereoRender(void)
RLAPI void rlPushMatrix(void)
RLAPI unsigned int rlGetTextureIdDefault(void)
RLAPI void rlViewport(int x, int y, int width, int height)
RLAPI void rlScissor(int x, int y, int width, int height)
RLAPI int rlGetVersion(void)
RLAPI void rlClearColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
RLAPI void rlSetUniformSampler(int locIndex, unsigned int textureId)
#define RL_PROJECTION
Definition: rlgl.h:254
RLAPI void rlSetMatrixProjectionStereo(Matrix right, Matrix left)
#define RL_TEXTURE_FILTER_LINEAR
Definition: rlgl.h:240
#define RL_TEXTURE_MIN_FILTER
Definition: rlgl.h:237
RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
RLAPI void rlEnableFramebuffer(unsigned int id)
RLAPI void rlEnableShader(unsigned int id)
RLAPI void rlMultMatrixf(float *matf)
RLAPI void rlDisableFramebuffer(void)
RLAPI void rlEnableScissorTest(void)
RLAPI void rlFrustum(double left, double right, double bottom, double top, double znear, double zfar)
RLAPI void rlSetMatrixViewOffsetStereo(Matrix right, Matrix left)
RLAPI void rlDrawRenderBatchActive(void)
RLAPI void rlEnableDepthTest(void)
RLAPI void rlSetShader(unsigned int id, int *locs)
RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName)
#define RL_TEXTURE_MAG_FILTER
Definition: rlgl.h:236
RLAPI void rlglInit(int width, int height)
RLAPI void rlTextureParameters(unsigned int id, int param, int value)
RLAPI void rlPopMatrix(void)
RLAPI void rlUnloadShaderProgram(unsigned int id)
RLAPI void rlMatrixMode(int mode)
RLAPI void rlDisableDepthTest(void)
#define TRACELOGD(...)
Definition: rlgl.h:130
RLAPI void rlSetFramebufferWidth(int width)
RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName)
#define MAX_TEXTFORMAT_BUFFERS
int sdefl_bound(int in_len)
int sdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl)
int sinflate(void *out, int cap, const void *in, int size)
unsigned int uint32_t
Definition: stdint.h:80
signed int int32_t
Definition: stdint.h:77
float rotation
Definition: raylib.h:313
Vector2 offset
Definition: raylib.h:311
Vector2 target
Definition: raylib.h:312
float zoom
Definition: raylib.h:314
Vector3 up
Definition: raylib.h:302
int projection
Definition: raylib.h:304
Vector3 position
Definition: raylib.h:300
float fovy
Definition: raylib.h:303
Vector3 target
Definition: raylib.h:301
Definition: raylib.h:220
unsigned char a
Definition: raylib.h:224
unsigned char b
Definition: raylib.h:223
unsigned char r
Definition: raylib.h:221
unsigned char g
Definition: raylib.h:222
char currentButtonState[MAX_MOUSE_BUTTONS]
Definition: rcore.c:456
double previous
Definition: rcore.c:488
const char * basePath
Definition: rcore.c:419
Point position
Definition: rcore.c:398
struct CoreData::@140::@145 Gamepad
int lastButtonPressed
Definition: rcore.c:473
char previousTouchState[MAX_TOUCH_POINTS]
Definition: rcore.c:470
struct CoreData::@140::@142 Keyboard
int pointId[MAX_TOUCH_POINTS]
Definition: rcore.c:467
Vector2 offset
Definition: rcore.c:447
int pointCount
Definition: rcore.c:466
Size display
Definition: rcore.c:399
char ** dropFilesPath
Definition: rcore.c:406
struct CoreData::@138 Window
float previousWheelMove
Definition: rcore.c:459
char currentTouchState[MAX_TOUCH_POINTS]
Definition: rcore.c:469
bool fullscreen
Definition: rcore.c:394
Size screen
Definition: rcore.c:400
float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]
Definition: rcore.c:479
Size render
Definition: rcore.c:402
bool cursorOnScreen
Definition: rcore.c:454
int charPressedQueueCount
Definition: rcore.c:434
bool resizedLastFrame
Definition: rcore.c:396
Matrix screenScale
Definition: rcore.c:404
bool ready
Definition: rcore.c:393
int cursor
Definition: rcore.c:452
struct CoreData::@141 Time
struct CoreData::@140 Input
int keyPressedQueueCount
Definition: rcore.c:431
int dropFileCount
Definition: rcore.c:407
double draw
Definition: rcore.c:490
char name[MAX_GAMEPADS][64]
Definition: rcore.c:476
char currentKeyState[MAX_KEYBOARD_KEYS]
Definition: rcore.c:427
int axisCount
Definition: rcore.c:474
char previousKeyState[MAX_KEYBOARD_KEYS]
Definition: rcore.c:428
int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]
Definition: rcore.c:430
struct CoreData::@139 Storage
double update
Definition: rcore.c:489
unsigned int flags
Definition: rcore.c:392
float currentWheelMove
Definition: rcore.c:458
const char * title
Definition: rcore.c:391
char previousButtonState[MAX_MOUSE_BUTTONS]
Definition: rcore.c:457
bool cursorHidden
Definition: rcore.c:453
Point renderOffset
Definition: rcore.c:403
bool shouldClose
Definition: rcore.c:395
Vector2 scale
Definition: rcore.c:448
Size currentFbo
Definition: rcore.c:401
int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]
Definition: rcore.c:433
double current
Definition: rcore.c:487
Vector2 currentPosition
Definition: rcore.c:449
struct CoreData::@140::@143 Mouse
Vector2 previousPosition
Definition: rcore.c:450
struct CoreData::@140::@144 Touch
int exitKey
Definition: rcore.c:426
double target
Definition: rcore.c:492
unsigned int frameCounter
Definition: rcore.c:496
double frame
Definition: rcore.c:491
Definition: dirent.h:93
Rectangle * recs
Definition: raylib.h:294
Gamepad input state.
Definition: glfw3.h:1879
Image data.
Definition: glfw3.h:1855
int height
Definition: glfw3.h:1861
unsigned char * pixels
Definition: glfw3.h:1864
int width
Definition: glfw3.h:1858
Video mode type.
Definition: glfw3.h:1792
int width
Definition: glfw3.h:1795
int refreshRate
Definition: glfw3.h:1810
int height
Definition: glfw3.h:1798
int touchAction
Definition: rgestures.h:106
int pointId[MAX_TOUCH_POINTS]
Definition: rgestures.h:108
Vector2 position[MAX_TOUCH_POINTS]
Definition: rgestures.h:109
int pointCount
Definition: rgestures.h:107
Definition: raylib.h:236
void * data
Definition: raylib.h:237
int format
Definition: raylib.h:241
int height
Definition: raylib.h:239
int width
Definition: raylib.h:238
Definition: raylib.h:212
HEADER ///.
Definition: msf_gif.h:75
void * data
Definition: msf_gif.h:76
size_t dataSize
Definition: msf_gif.h:77
Definition: rcore.c:363
int y
Definition: rcore.c:363
int x
Definition: rcore.c:363
Definition: raylib.h:400
Vector3 position
Definition: raylib.h:401
Vector3 direction
Definition: raylib.h:402
float height
Definition: raylib.h:232
float x
Definition: raylib.h:229
float y
Definition: raylib.h:230
float width
Definition: raylib.h:231
Texture texture
Definition: raylib.h:262
unsigned int id
Definition: raylib.h:261
Definition: raylib.h:343
int * locs
Definition: raylib.h:345
unsigned int id
Definition: raylib.h:344
Definition: rcore.c:364
unsigned int height
Definition: rcore.c:364
unsigned int width
Definition: rcore.c:364
int width
Definition: raylib.h:247
int height
Definition: raylib.h:248
unsigned int id
Definition: raylib.h:246
float x
Definition: physac.h:130
float y
Definition: physac.h:131
float x
Definition: raylib.h:195
float y
Definition: raylib.h:196
float z
Definition: raylib.h:197
float x
Definition: raylib.h:202
float y
Definition: raylib.h:203
float w
Definition: raylib.h:205
float z
Definition: raylib.h:204
float lensDistortionValues[4]
Definition: raylib.h:469
float lensSeparationDistance
Definition: raylib.h:467
float interpupillaryDistance
Definition: raylib.h:468
float hScreenSize
Definition: raylib.h:463
float vScreenSize
Definition: raylib.h:464
int vResolution
Definition: raylib.h:462
float eyeToScreenDistance
Definition: raylib.h:466
int hResolution
Definition: raylib.h:461
float leftScreenCenter[2]
Definition: raylib.h:479
float scale[2]
Definition: raylib.h:481
float scaleIn[2]
Definition: raylib.h:482
float leftLensCenter[2]
Definition: raylib.h:477
float rightLensCenter[2]
Definition: raylib.h:478
Matrix viewOffset[2]
Definition: raylib.h:476
Matrix projection[2]
Definition: raylib.h:475
float rightScreenCenter[2]
Definition: raylib.h:480
Definition: dirent.h:40
char * d_name
Definition: dirent.h:41
Definition: sdefl.h:157
GLFWAPI HWND glfwGetWin32Window(GLFWwindow *handle)