Merge branch 'raysan5:master' into test

This commit is contained in:
MULTi 2025-12-28 22:24:43 +05:30 • committed by GitHub
commit b0ea362bcb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 20544 additions and 5252 deletions

View file

@ -228,7 +228,6 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
ifeq ($(ANDROID_ARCH),x86_64)
ANDROID_COMPILER_ARCH = x86_64
endif
endif
# Define raylib graphics api depending on selected platform
@ -261,17 +260,17 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32)
#GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE)
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DRM)
# On DRM OpenGL ES 2.0 must be used
GRAPHICS = GRAPHICS_API_OPENGL_ES2
GRAPHICS ?= GRAPHICS_API_OPENGL_ES2
#GRAPHICS = GRAPHICS_API_OPENGL_11_SOFTWARE # Uncomment to use software rendering
endif
ifeq ($(TARGET_PLATFORM),$(filter $(TARGET_PLATFORM),PLATFORM_WEB PLATFORM_WEB_RGFW))
# On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0
GRAPHICS = GRAPHICS_API_OPENGL_ES2
GRAPHICS ?= GRAPHICS_API_OPENGL_ES2
#GRAPHICS = GRAPHICS_API_OPENGL_ES3
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
# By default use OpenGL ES 2.0 on Android
GRAPHICS = GRAPHICS_API_OPENGL_ES2
GRAPHICS ?= GRAPHICS_API_OPENGL_ES2
endif
# Define default C compiler and archiver to pack library: CC, AR
@ -407,7 +406,7 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
endif
ifeq ($(ANDROID_ARCH),arm64)
CFLAGS += -target aarch64 -mfix-cortex-a53-835769
CFLAGS += -mfix-cortex-a53-835769
endif
ifeq ($(ANDROID_ARCH),x86)
CFLAGS += -march=i686
@ -636,13 +635,19 @@ ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_RGFW)
endif
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DRM)
LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl
LDLIBS = -ldrm -lgbm -lpthread -lrt -lm -ldl
ifeq ($(GRAPHICS),GRAPHICS_API_OPENGL_ES2)
LDLIBS += -lGLESv2 -lEGL
endif
ifeq ($(RAYLIB_MODULE_AUDIO),TRUE)
LDLIBS += -latomic
endif
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_DESKTOP_WIN32)
LDLIBS = -lgdi32 -lwinmm -lopengl32 -lshcore
LDLIBS = -lgdi32 -lwinmm -lshcore
ifneq ($(GRAPHICS),GRAPHICS_API_OPENGL_11_SOFTWARE)
LDLIBS += -lopengl32
endif
endif
ifeq ($(TARGET_PLATFORM),PLATFORM_ANDROID)
LDLIBS = -llog -landroid -lEGL -lGLESv2 -lOpenSLES -lc -lm

View file

@ -49,19 +49,18 @@
#define SUPPORT_RPRAND_GENERATOR 1
// Mouse gestures are directly mapped like touches and processed by gestures system
#define SUPPORT_MOUSE_GESTURES 1
// Reconfigure standard input to receive key inputs, works with SSH connection.
// Reconfigure standard input to receive key inputs, works with SSH connection
#define SUPPORT_SSH_KEYBOARD_RPI 1
// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
#define SUPPORT_WINMM_HIGHRES_TIMER 1
// Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used
//#define SUPPORT_BUSY_WAIT_LOOP 1
// Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy
#define SUPPORT_PARTIALBUSY_WAIT_LOOP 1
// Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
// WARNING: It also requires SUPPORT_IMAGE_EXPORT and SUPPORT_FILEFORMAT_PNG flags
#define SUPPORT_SCREEN_CAPTURE 1
// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
#define SUPPORT_GIF_RECORDING 1
// Support CompressData() and DecompressData() functions
#define SUPPORT_COMPRESSION_API 1
// Support automatic generated events, loading and recording of those events when required
@ -76,7 +75,7 @@
#define SUPPORT_CLIPBOARD_IMAGE 1
// NOTE: Clipboard image loading requires support for some image file formats
// TODO: Those defines should probably be removed from here, I prefer to let the user manage them
// TODO: Those defines should probably be removed from here, letting the user manage them
#if defined(SUPPORT_CLIPBOARD_IMAGE)
#ifndef SUPPORT_MODULE_RTEXTURES
#define SUPPORT_MODULE_RTEXTURES 1
@ -226,7 +225,7 @@
// On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
// at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
// drawing text and shapes with a single draw call [SetShapesTexture()].
// drawing text and shapes with a single draw call [SetShapesTexture()]
#define SUPPORT_FONT_ATLAS_WHITE_REC 1
// Support conservative font atlas size estimation

717
src/external/msf_gif.h vendored
View file

@ -1,717 +0,0 @@
/*
HOW TO USE:
In exactly one translation unit (.c or .cpp file), #define MSF_GIF_IMPL before including the header, like so:
#define MSF_GIF_IMPL
#include "msf_gif.h"
Everywhere else, just include the header like normal.
USAGE EXAMPLE:
int width = 480, height = 320, centisecondsPerFrame = 5, bitDepth = 16;
MsfGifState gifState = {};
// msf_gif_bgra_flag = true; //optionally, set this flag if your pixels are in BGRA format instead of RGBA
// msf_gif_alpha_threshold = 128; //optionally, enable transparency (see function documentation below for details)
msf_gif_begin(&gifState, width, height);
msf_gif_frame(&gifState, ..., centisecondsPerFrame, bitDepth, width * 4); //frame 1
msf_gif_frame(&gifState, ..., centisecondsPerFrame, bitDepth, width * 4); //frame 2
msf_gif_frame(&gifState, ..., centisecondsPerFrame, bitDepth, width * 4); //frame 3, etc...
MsfGifResult result = msf_gif_end(&gifState);
if (result.data) {
FILE * fp = fopen("MyGif.gif", "wb");
fwrite(result.data, result.dataSize, 1, fp);
fclose(fp);
}
msf_gif_free(result);
Detailed function documentation can be found in the header section below.
ERROR HANDLING:
If memory allocation fails, the functions will signal the error via their return values.
If one function call fails, the library will free all of its allocations,
and all subsequent calls will safely no-op and return 0 until the next call to `msf_gif_begin()`.
Therefore, it's safe to check only the return value of `msf_gif_end()`.
REPLACING MALLOC:
This library uses malloc+realloc+free internally for memory allocation.
To facilitate integration with custom memory allocators, these calls go through macros, which can be redefined.
The expected function signature equivalents of the macros are as follows:
void * MSF_GIF_MALLOC(void * context, size_t newSize)
void * MSF_GIF_REALLOC(void * context, void * oldMemory, size_t oldSize, size_t newSize)
void MSF_GIF_FREE(void * context, void * oldMemory, size_t oldSize)
If your allocator needs a context pointer, you can set the `customAllocatorContext` field of the MsfGifState struct
before calling msf_gif_begin(), and it will be passed to all subsequent allocator macro calls.
The maximum number of bytes the library will allocate to encode a single gif is bounded by the following formula:
`(2 * 1024 * 1024) + (width * height * 8) + ((1024 + width * height * 1.5) * 3 * frameCount)`
The peak heap memory usage in bytes, if using a general-purpose heap allocator, is bounded by the following formula:
`(2 * 1024 * 1024) + (width * height * 9.5) + 1024 + (16 * frameCount) + (2 * sizeOfResultingGif)
See end of file for license information.
*/
//version 2.2
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// HEADER ///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef MSF_GIF_H
#define MSF_GIF_H
#include <stdint.h>
#include <stddef.h>
typedef struct {
void * data;
size_t dataSize;
size_t allocSize; //internal use
void * contextPointer; //internal use
} MsfGifResult;
typedef struct { //internal use
uint32_t * pixels;
int depth, count, rbits, gbits, bbits;
} MsfCookedFrame;
typedef struct MsfGifBuffer {
struct MsfGifBuffer * next;
size_t size;
uint8_t data[1];
} MsfGifBuffer;
typedef size_t (* MsfGifFileWriteFunc) (const void * buffer, size_t size, size_t count, void * stream);
typedef struct {
MsfGifFileWriteFunc fileWriteFunc;
void * fileWriteData;
MsfCookedFrame previousFrame;
MsfCookedFrame currentFrame;
int16_t * lzwMem;
MsfGifBuffer * listHead;
MsfGifBuffer * listTail;
int width, height;
void * customAllocatorContext;
int framesSubmitted; //needed for transparency to work correctly (because we reach into the previous frame)
} MsfGifState;
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
/**
* @param width Image width in pixels.
* @param height Image height in pixels.
* @return Non-zero on success, 0 on error.
*/
int msf_gif_begin(MsfGifState * handle, int width, int height);
/**
* @param pixelData Pointer to raw framebuffer data. Rows must be contiguous in memory, in RGBA8 format
* (or BGRA8 if you have set `msf_gif_bgra_flag = true`).
* Note: This function does NOT free `pixelData`. You must free it yourself afterwards.
* @param centiSecondsPerFrame How many hundredths of a second this frame should be displayed for.
* Note: This being specified in centiseconds is a limitation of the GIF format.
* @param maxBitDepth Limits how many bits per pixel can be used when quantizing the gif.
* The actual bit depth chosen for a given frame will be less than or equal to
* the supplied maximum, depending on the variety of colors used in the frame.
* `maxBitDepth` will be clamped between 1 and 16. The recommended default is 16.
* Lowering this value can result in faster exports and smaller gifs,
* but the quality may suffer.
* Please experiment with this value to find what works best for your application.
* @param pitchInBytes The number of bytes from the beginning of one row of pixels to the beginning of the next.
* If you want to flip the image, just pass in a negative pitch.
* @return Non-zero on success, 0 on error.
*/
int msf_gif_frame(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes);
/**
* @return A block of memory containing the gif file data, or NULL on error.
* You are responsible for freeing this via `msf_gif_free()`.
*/
MsfGifResult msf_gif_end(MsfGifState * handle);
/**
* @param result The MsfGifResult struct, verbatim as it was returned from `msf_gif_end()`.
*/
void msf_gif_free(MsfGifResult result);
//The gif format only supports 1-bit transparency, meaning a pixel will either be fully transparent or fully opaque.
//Pixels with an alpha value less than the alpha threshold will be treated as transparent.
//To enable exporting transparent gifs, set it to a value between 1 and 255 (inclusive) before calling msf_gif_frame().
//Setting it to 0 causes the alpha channel to be ignored. Its initial value is 0.
extern int msf_gif_alpha_threshold;
//Set `msf_gif_bgra_flag = true` before calling `msf_gif_frame()` if your pixels are in BGRA byte order instead of RBGA.
extern int msf_gif_bgra_flag;
//TO-FILE FUNCTIONS
//These functions are equivalent to the ones above, but they write results to a file incrementally,
//instead of building a buffer in memory. This can result in lower memory usage when saving large gifs,
//because memory usage is bounded by only the size of a single frame, and is not dependent on the number of frames.
//There is currently no reason to use these unless you are on a memory-constrained platform.
//If in doubt about which API to use, for now you should use the normal (non-file) functions above.
//The signature of MsfGifFileWriteFunc matches fwrite for convenience, so that you can use the C file API like so:
// FILE * fp = fopen("MyGif.gif", "wb");
// msf_gif_begin_to_file(&handle, width, height, (MsfGifFileWriteFunc) fwrite, (void *) fp);
// msf_gif_frame_to_file(...)
// msf_gif_end_to_file(&handle);
// fclose(fp);
//If you use a custom file write function, you must take care to return the same values that fwrite() would return.
//Note that all three functions will potentially write to the file.
int msf_gif_begin_to_file(MsfGifState * handle, int width, int height, MsfGifFileWriteFunc func, void * filePointer);
int msf_gif_frame_to_file(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes);
int msf_gif_end_to_file(MsfGifState * handle); //returns 0 on error and non-zero on success
#ifdef __cplusplus
}
#endif //__cplusplus
#endif //MSF_GIF_H
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// IMPLEMENTATION ///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef MSF_GIF_IMPL
#ifndef MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
#define MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
//ensure the library user has either defined all of malloc/realloc/free, or none
#if defined(MSF_GIF_MALLOC) && defined(MSF_GIF_REALLOC) && defined(MSF_GIF_FREE) //ok
#elif !defined(MSF_GIF_MALLOC) && !defined(MSF_GIF_REALLOC) && !defined(MSF_GIF_FREE) //ok
#else
#error "You must either define all of MSF_GIF_MALLOC, MSF_GIF_REALLOC, and MSF_GIF_FREE, or define none of them"
#endif
//provide default allocator definitions that redirect to the standard global allocator
#if !defined(MSF_GIF_MALLOC)
#include <stdlib.h> //malloc, etc.
#define MSF_GIF_MALLOC(contextPointer, newSize) malloc(newSize)
#define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) realloc(oldMemory, newSize)
#define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) free(oldMemory)
#endif
//instrumentation for capturing profiling traces (useless for the library user, but useful for the library author)
#ifdef MSF_GIF_ENABLE_TRACING
#define MsfTimeFunc TimeFunc
#define MsfTimeLoop TimeLoop
#define msf_init_profiling_thread init_profiling_thread
#else
#define MsfTimeFunc
#define MsfTimeLoop(name)
#define msf_init_profiling_thread()
#endif //MSF_GIF_ENABLE_TRACING
#include <string.h> //memcpy
//TODO: use compiler-specific notation to force-inline functions currently marked inline
#if defined(__GNUC__) //gcc, clang
static inline int msf_bit_log(int i) { return 32 - __builtin_clz(i); }
#elif defined(_MSC_VER) //msvc
#include <intrin.h>
static inline int msf_bit_log(int i) { unsigned long idx; _BitScanReverse(&idx, i); return idx + 1; }
#else //fallback implementation for other compilers
//from https://stackoverflow.com/a/31718095/3064745 - thanks!
static inline int msf_bit_log(int i) {
static const int MultiplyDeBruijnBitPosition[32] = {
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31,
};
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
return MultiplyDeBruijnBitPosition[(uint32_t)(i * 0x07C4ACDDU) >> 27] + 1;
}
#endif
static inline int msf_imin(int a, int b) { return a < b? a : b; }
static inline int msf_imax(int a, int b) { return b < a? a : b; }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Frame Cooking ///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if (defined (__SSE2__) || defined (_M_X64) || _M_IX86_FP == 2) && !defined(MSF_GIF_NO_SSE2)
#include <emmintrin.h>
#endif
int msf_gif_alpha_threshold = 0;
int msf_gif_bgra_flag = 0;
static void msf_cook_frame(MsfCookedFrame * frame, uint8_t * raw, uint8_t * used,
int width, int height, int pitch, int depth)
{ MsfTimeFunc
//bit depth for each channel
static const int rdepthsArray[17] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 };
static const int gdepthsArray[17] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6 };
static const int bdepthsArray[17] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 };
//this extra level of indirection looks unnecessary but we need to explicitly decay the arrays to pointers
//in order to be able to swap them because of C's annoying not-quite-pointers, not-quite-value-types stack arrays.
const int * rdepths = msf_gif_bgra_flag? bdepthsArray : rdepthsArray;
const int * gdepths = gdepthsArray;
const int * bdepths = msf_gif_bgra_flag? rdepthsArray : bdepthsArray;
static const int ditherKernel[16] = {
0 << 12, 8 << 12, 2 << 12, 10 << 12,
12 << 12, 4 << 12, 14 << 12, 6 << 12,
3 << 12, 11 << 12, 1 << 12, 9 << 12,
15 << 12, 7 << 12, 13 << 12, 5 << 12,
};
uint32_t * cooked = frame->pixels;
int count = 0;
MsfTimeLoop("do") do {
int rbits = rdepths[depth], gbits = gdepths[depth], bbits = bdepths[depth];
int paletteSize = (1 << (rbits + gbits + bbits)) + 1;
memset(used, 0, paletteSize * sizeof(uint8_t));
//TODO: document what this math does and why it's correct
int rdiff = (1 << (8 - rbits)) - 1;
int gdiff = (1 << (8 - gbits)) - 1;
int bdiff = (1 << (8 - bbits)) - 1;
short rmul = (short) ((255.0f - rdiff) / 255.0f * 257);
short gmul = (short) ((255.0f - gdiff) / 255.0f * 257);
short bmul = (short) ((255.0f - bdiff) / 255.0f * 257);
int gmask = ((1 << gbits) - 1) << rbits;
int bmask = ((1 << bbits) - 1) << rbits << gbits;
MsfTimeLoop("cook") for (int y = 0; y < height; ++y) {
int x = 0;
#if (defined (__SSE2__) || defined (_M_X64) || _M_IX86_FP == 2) && !defined(MSF_GIF_NO_SSE2)
__m128i k = _mm_loadu_si128((__m128i *) &ditherKernel[(y & 3) * 4]);
__m128i k2 = _mm_or_si128(_mm_srli_epi32(k, rbits), _mm_slli_epi32(_mm_srli_epi32(k, bbits), 16));
for (; x < width - 3; x += 4) {
uint8_t * pixels = &raw[y * pitch + x * 4];
__m128i p = _mm_loadu_si128((__m128i *) pixels);
__m128i rb = _mm_and_si128(p, _mm_set1_epi32(0x00FF00FF));
__m128i rb1 = _mm_mullo_epi16(rb, _mm_set_epi16(bmul, rmul, bmul, rmul, bmul, rmul, bmul, rmul));
__m128i rb2 = _mm_adds_epu16(rb1, k2);
__m128i r3 = _mm_srli_epi32(_mm_and_si128(rb2, _mm_set1_epi32(0x0000FFFF)), 16 - rbits);
__m128i b3 = _mm_and_si128(_mm_srli_epi32(rb2, 32 - rbits - gbits - bbits), _mm_set1_epi32(bmask));
__m128i g = _mm_and_si128(_mm_srli_epi32(p, 8), _mm_set1_epi32(0x000000FF));
__m128i g1 = _mm_mullo_epi16(g, _mm_set1_epi32(gmul));
__m128i g2 = _mm_adds_epu16(g1, _mm_srli_epi32(k, gbits));
__m128i g3 = _mm_and_si128(_mm_srli_epi32(g2, 16 - rbits - gbits), _mm_set1_epi32(gmask));
__m128i out = _mm_or_si128(_mm_or_si128(r3, g3), b3);
//mask in transparency based on threshold
//NOTE: we can theoretically do a sub instead of srli by doing an unsigned compare via bias
// to maybe save a TINY amount of throughput? but lol who cares maybe I'll do it later -m
__m128i invAlphaMask = _mm_cmplt_epi32(_mm_srli_epi32(p, 24), _mm_set1_epi32(msf_gif_alpha_threshold));
out = _mm_or_si128(_mm_and_si128(invAlphaMask, _mm_set1_epi32(paletteSize - 1)), _mm_andnot_si128(invAlphaMask, out));
//TODO: does storing this as a __m128i then reading it back as a uint32_t violate strict aliasing?
uint32_t * c = &cooked[y * width + x];
_mm_storeu_si128((__m128i *) c, out);
}
#endif
//scalar cleanup loop
for (; x < width; ++x) {
uint8_t * p = &raw[y * pitch + x * 4];
//transparent pixel if alpha is low
if (p[3] < msf_gif_alpha_threshold) {
cooked[y * width + x] = paletteSize - 1;
continue;
}
int dx = x & 3, dy = y & 3;
int k = ditherKernel[dy * 4 + dx];
cooked[y * width + x] =
(msf_imin(65535, p[2] * bmul + (k >> bbits)) >> (16 - rbits - gbits - bbits) & bmask) |
(msf_imin(65535, p[1] * gmul + (k >> gbits)) >> (16 - rbits - gbits ) & gmask) |
msf_imin(65535, p[0] * rmul + (k >> rbits)) >> (16 - rbits );
}
}
count = 0;
MsfTimeLoop("mark") for (int i = 0; i < width * height; ++i) {
used[cooked[i]] = 1;
}
//count used colors, transparent is ignored
MsfTimeLoop("count") for (int j = 0; j < paletteSize - 1; ++j) {
count += used[j];
}
} while (count >= 256 && --depth);
MsfCookedFrame ret = { cooked, depth, count, rdepths[depth], gdepths[depth], bdepths[depth] };
*frame = ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Frame Compression ///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static inline void msf_put_code(uint8_t * * writeHead, uint32_t * blockBits, int len, uint32_t code) {
//insert new code into block buffer
int idx = *blockBits / 8;
int bit = *blockBits % 8;
(*writeHead)[idx + 0] |= code << bit ;
(*writeHead)[idx + 1] |= code >> ( 8 - bit);
(*writeHead)[idx + 2] |= code >> (16 - bit);
*blockBits += len;
//prep the next block buffer if the current one is full
if (*blockBits >= 256 * 8) {
*blockBits -= 255 * 8;
(*writeHead) += 256;
(*writeHead)[2] = (*writeHead)[1];
(*writeHead)[1] = (*writeHead)[0];
(*writeHead)[0] = 255;
memset((*writeHead) + 4, 0, 256);
}
}
typedef struct {
int16_t * data;
int len;
int stride;
} MsfStridedList;
static inline void msf_lzw_reset(MsfStridedList * lzw, int tableSize, int stride) { MsfTimeFunc
memset(lzw->data, 0xFF, 4096 * stride * sizeof(int16_t));
lzw->len = tableSize + 2;
lzw->stride = stride;
}
static MsfGifBuffer * msf_compress_frame(void * allocContext, int width, int height, int centiSeconds,
MsfCookedFrame frame, MsfGifState * handle, uint8_t * used, int16_t * lzwMem)
{ MsfTimeFunc
//NOTE: we reserve enough memory for theoretical the worst case upfront because it's a reasonable amount,
// and prevents us from ever having to check size or realloc during compression
int maxBufSize = offsetof(MsfGifBuffer, data) + 32 + 256 * 3 + width * height * 3 / 2; //headers + color table + data
MsfGifBuffer * buffer = (MsfGifBuffer *) MSF_GIF_MALLOC(allocContext, maxBufSize);
if (!buffer) { return NULL; }
uint8_t * writeHead = buffer->data;
MsfStridedList lzw = { lzwMem, 0, 0 };
//allocate tlb
int totalBits = frame.rbits + frame.gbits + frame.bbits;
int tlbSize = (1 << totalBits) + 1;
uint8_t tlb[(1 << 16) + 1]; //only 64k, so stack allocating is fine
//generate palette
typedef struct { uint8_t r, g, b; } Color3;
Color3 table[256] = { {0} };
int tableIdx = 1; //we start counting at 1 because 0 is the transparent color
//transparent is always last in the table
tlb[tlbSize-1] = 0;
MsfTimeLoop("table") for (int i = 0; i < tlbSize-1; ++i) {
if (used[i]) {
tlb[i] = tableIdx;
int rmask = (1 << frame.rbits) - 1;
int gmask = (1 << frame.gbits) - 1;
//isolate components
int r = i & rmask;
int g = i >> frame.rbits & gmask;
int b = i >> (frame.rbits + frame.gbits);
//shift into highest bits
r <<= 8 - frame.rbits;
g <<= 8 - frame.gbits;
b <<= 8 - frame.bbits;
table[tableIdx].r = r | r >> frame.rbits | r >> (frame.rbits * 2) | r >> (frame.rbits * 3);
table[tableIdx].g = g | g >> frame.gbits | g >> (frame.gbits * 2) | g >> (frame.gbits * 3);
table[tableIdx].b = b | b >> frame.bbits | b >> (frame.bbits * 2) | b >> (frame.bbits * 3);
if (msf_gif_bgra_flag) {
uint8_t temp = table[tableIdx].r;
table[tableIdx].r = table[tableIdx].b;
table[tableIdx].b = temp;
}
++tableIdx;
}
}
int hasTransparentPixels = used[tlbSize-1];
//SPEC: "Because of some algorithmic constraints however, black & white images which have one color bit
// must be indicated as having a code size of 2."
int tableBits = msf_imax(2, msf_bit_log(tableIdx - 1));
int tableSize = 1 << tableBits;
//NOTE: we don't just compare `depth` field here because it will be wrong for the first frame and we will segfault
MsfCookedFrame previous = handle->previousFrame;
int hasSamePal = frame.rbits == previous.rbits && frame.gbits == previous.gbits && frame.bbits == previous.bbits;
int framesCompatible = hasSamePal && !hasTransparentPixels;
//NOTE: because __attribute__((__packed__)) is annoyingly compiler-specific, we do this unreadable weirdness
char headerBytes[19] = "\x21\xF9\x04\x05\0\0\0\0" "\x2C\0\0\0\0\0\0\0\0\x80";
//NOTE: we need to check the frame number because if we reach into the buffer prior to the first frame,
// we'll just clobber the file header instead, which is a bug
if (hasTransparentPixels && handle->framesSubmitted > 0) {
handle->listTail->data[3] = 0x09; //set the previous frame's disposal to background, so transparency is possible
}
memcpy(&headerBytes[4], &centiSeconds, 2);
memcpy(&headerBytes[13], &width, 2);
memcpy(&headerBytes[15], &height, 2);
headerBytes[17] |= tableBits - 1;
memcpy(writeHead, headerBytes, 18);
writeHead += 18;
//local color table
memcpy(writeHead, table, tableSize * sizeof(Color3));
writeHead += tableSize * sizeof(Color3);
*writeHead++ = tableBits;
//prep block
memset(writeHead, 0, 260);
writeHead[0] = 255;
uint32_t blockBits = 8; //relative to block.head
//SPEC: "Encoders should output a Clear code as the first code of each image data stream."
msf_lzw_reset(&lzw, tableSize, tableIdx);
msf_put_code(&writeHead, &blockBits, msf_bit_log(lzw.len - 1), tableSize);
int lastCode = framesCompatible && frame.pixels[0] == previous.pixels[0]? 0 : tlb[frame.pixels[0]];
MsfTimeLoop("compress") for (int i = 1; i < width * height; ++i) {
//PERF: branching vs. branchless version of this line is observed to have no discernable impact on speed
int color = framesCompatible && frame.pixels[i] == previous.pixels[i]? 0 : tlb[frame.pixels[i]];
int code = (&lzw.data[lastCode * lzw.stride])[color];
if (code < 0) {
//write to code stream
int codeBits = msf_bit_log(lzw.len - 1);
msf_put_code(&writeHead, &blockBits, codeBits, lastCode);
if (lzw.len > 4095) {
//reset buffer code table
msf_put_code(&writeHead, &blockBits, codeBits, tableSize);
msf_lzw_reset(&lzw, tableSize, tableIdx);
} else {
(&lzw.data[lastCode * lzw.stride])[color] = lzw.len;
++lzw.len;
}
lastCode = color;
} else {
lastCode = code;
}
}
//write code for leftover index buffer contents, then the end code
msf_put_code(&writeHead, &blockBits, msf_imin(12, msf_bit_log(lzw.len - 1)), lastCode);
msf_put_code(&writeHead, &blockBits, msf_imin(12, msf_bit_log(lzw.len)), tableSize + 1);
//flush remaining data
if (blockBits > 8) {
int bytes = (blockBits + 7) / 8; //round up
writeHead[0] = bytes - 1;
writeHead += bytes;
}
*writeHead++ = 0; //terminating block
//fill in buffer header and shrink buffer to fit data
buffer->next = NULL;
buffer->size = writeHead - buffer->data;
MsfGifBuffer * moved =
(MsfGifBuffer *) MSF_GIF_REALLOC(allocContext, buffer, maxBufSize, offsetof(MsfGifBuffer, data) + buffer->size);
if (!moved) { MSF_GIF_FREE(allocContext, buffer, maxBufSize); return NULL; }
return moved;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// To-memory API ///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static const int lzwAllocSize = 4096 * 256 * sizeof(int16_t);
//NOTE: by C standard library conventions, freeing NULL should be a no-op,
// but just in case the user's custom free doesn't follow that rule, we do null checks on our end as well.
static void msf_free_gif_state(MsfGifState * handle) {
if (handle->previousFrame.pixels) MSF_GIF_FREE(handle->customAllocatorContext, handle->previousFrame.pixels,
handle->width * handle->height * sizeof(uint32_t));
if (handle->currentFrame.pixels) MSF_GIF_FREE(handle->customAllocatorContext, handle->currentFrame.pixels,
handle->width * handle->height * sizeof(uint32_t));
if (handle->lzwMem) MSF_GIF_FREE(handle->customAllocatorContext, handle->lzwMem, lzwAllocSize);
for (MsfGifBuffer * node = handle->listHead; node;) {
MsfGifBuffer * next = node->next; //NOTE: we have to copy the `next` pointer BEFORE freeing the node holding it
MSF_GIF_FREE(handle->customAllocatorContext, node, offsetof(MsfGifBuffer, data) + node->size);
node = next;
}
handle->listHead = NULL; //this implicitly marks the handle as invalid until the next msf_gif_begin() call
}
int msf_gif_begin(MsfGifState * handle, int width, int height) { MsfTimeFunc
//NOTE: we cannot stomp the entire struct to zero because we must preserve `customAllocatorContext`.
MsfCookedFrame empty = {0}; //god I hate MSVC...
handle->previousFrame = empty;
handle->currentFrame = empty;
handle->width = width;
handle->height = height;
handle->framesSubmitted = 0;
//allocate memory for LZW buffer
//NOTE: Unfortunately we can't just use stack memory for the LZW table because it's 2MB,
// which is more stack space than most operating systems give by default,
// and we can't realistically expect users to be willing to override that just to use our library,
// so we have to allocate this on the heap.
handle->lzwMem = (int16_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, lzwAllocSize);
handle->previousFrame.pixels =
(uint32_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, handle->width * handle->height * sizeof(uint32_t));
handle->currentFrame.pixels =
(uint32_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, handle->width * handle->height * sizeof(uint32_t));
//setup header buffer header (lol)
handle->listHead = (MsfGifBuffer *) MSF_GIF_MALLOC(handle->customAllocatorContext, offsetof(MsfGifBuffer, data) + 32);
if (!handle->listHead || !handle->lzwMem || !handle->previousFrame.pixels || !handle->currentFrame.pixels) {
msf_free_gif_state(handle);
return 0;
}
handle->listTail = handle->listHead;
handle->listHead->next = NULL;
handle->listHead->size = 32;
//NOTE: because __attribute__((__packed__)) is annoyingly compiler-specific, we do this unreadable weirdness
char headerBytes[33] = "GIF89a\0\0\0\0\x70\0\0" "\x21\xFF\x0BNETSCAPE2.0\x03\x01\0\0\0";
memcpy(&headerBytes[6], &width, 2);
memcpy(&headerBytes[8], &height, 2);
memcpy(handle->listHead->data, headerBytes, 32);
return 1;
}
int msf_gif_frame(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes)
{ MsfTimeFunc
if (!handle->listHead) { return 0; }
maxBitDepth = msf_imax(1, msf_imin(16, maxBitDepth));
if (pitchInBytes == 0) pitchInBytes = handle->width * 4;
if (pitchInBytes < 0) pixelData -= pitchInBytes * (handle->height - 1);
uint8_t used[(1 << 16) + 1]; //only 64k, so stack allocating is fine
msf_cook_frame(&handle->currentFrame, pixelData, used, handle->width, handle->height, pitchInBytes,
msf_imin(maxBitDepth, handle->previousFrame.depth + 160 / msf_imax(1, handle->previousFrame.count)));
MsfGifBuffer * buffer = msf_compress_frame(handle->customAllocatorContext, handle->width, handle->height,
centiSecondsPerFame, handle->currentFrame, handle, used, handle->lzwMem);
if (!buffer) { msf_free_gif_state(handle); return 0; }
handle->listTail->next = buffer;
handle->listTail = buffer;
//swap current and previous frames
MsfCookedFrame tmp = handle->previousFrame;
handle->previousFrame = handle->currentFrame;
handle->currentFrame = tmp;
handle->framesSubmitted += 1;
return 1;
}
MsfGifResult msf_gif_end(MsfGifState * handle) { MsfTimeFunc
if (!handle->listHead) { MsfGifResult empty = {0}; return empty; }
//first pass: determine total size
size_t total = 1; //1 byte for trailing marker
for (MsfGifBuffer * node = handle->listHead; node; node = node->next) { total += node->size; }
//second pass: write data
uint8_t * buffer = (uint8_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, total);
if (buffer) {
uint8_t * writeHead = buffer;
for (MsfGifBuffer * node = handle->listHead; node; node = node->next) {
memcpy(writeHead, node->data, node->size);
writeHead += node->size;
}
*writeHead++ = 0x3B;
}
//third pass: free buffers
msf_free_gif_state(handle);
MsfGifResult ret = { buffer, total, total, handle->customAllocatorContext };
return ret;
}
void msf_gif_free(MsfGifResult result) { MsfTimeFunc
if (result.data) { MSF_GIF_FREE(result.contextPointer, result.data, result.allocSize); }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// To-file API ///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int msf_gif_begin_to_file(MsfGifState * handle, int width, int height, MsfGifFileWriteFunc func, void * filePointer) {
handle->fileWriteFunc = func;
handle->fileWriteData = filePointer;
return msf_gif_begin(handle, width, height);
}
int msf_gif_frame_to_file(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes) {
if (!msf_gif_frame(handle, pixelData, centiSecondsPerFame, maxBitDepth, pitchInBytes)) { return 0; }
//NOTE: this is a somewhat hacky implementation which is not perfectly efficient, but it's good enough for now
MsfGifBuffer * head = handle->listHead;
if (!handle->fileWriteFunc(head->data, head->size, 1, handle->fileWriteData)) { msf_free_gif_state(handle); return 0; }
handle->listHead = head->next;
MSF_GIF_FREE(handle->customAllocatorContext, head, offsetof(MsfGifBuffer, data) + head->size);
return 1;
}
int msf_gif_end_to_file(MsfGifState * handle) {
//NOTE: this is a somewhat hacky implementation which is not perfectly efficient, but it's good enough for now
MsfGifResult result = msf_gif_end(handle);
int ret = (int) handle->fileWriteFunc(result.data, result.dataSize, 1, handle->fileWriteData);
msf_gif_free(result);
return ret;
}
#endif //MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
#endif //MSF_GIF_IMPL
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2021 Miles Fogle
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

View file

@ -308,7 +308,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
unsigned char alpha = 0;
// NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
for (int i = 0; i < image_pixel_size; i++)
for (int i = 0; i < data_size/sizeof(unsigned short); i++)
{
alpha = ((unsigned short *)image_data)[i] >> 15;
((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 1;
@ -328,7 +328,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
unsigned char alpha = 0;
// NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
for (int i = 0; i < image_pixel_size; i++)
for (int i = 0; i < data_size/sizeof(unsigned short); i++)
{
alpha = ((unsigned short *)image_data)[i] >> 12;
((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 4;
@ -339,7 +339,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
}
}
}
else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24)) // DDS_RGB, no compressed
else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24)) // DDS_RGB, no compressed
{
int data_size = image_pixel_size*3*sizeof(unsigned char);
if (header->mipmap_count > 1) data_size = data_size + data_size/3;
@ -362,7 +362,7 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_
// NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment)
// DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA
// So, we must realign B8G8R8A8 to R8G8B8A8
for (int i = 0; i < image_pixel_size*4; i += 4)
for (int i = 0; i < data_size; i += 4)
{
blue = ((unsigned char *)image_data)[i];
((unsigned char *)image_data)[i] = ((unsigned char *)image_data)[i + 2];

1033
src/external/rlsw.h vendored

File diff suppressed because it is too large Load diff

View file

@ -198,7 +198,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl);
static int
sdefl_ilog2(int n) {
if (!n) return 0;
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(__llvm__) && !defined(__INTEL_COMPILER) // @raysan5, address PR #5367
unsigned long msbp = 0;
_BitScanReverse(&msbp, (unsigned long)n);
return (int)msbp;

View file

@ -171,7 +171,7 @@ extern int zsinflate(void *out, int cap, const void *in, int size);
static int
sinfl_bsr(unsigned n) {
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(__llvm__) && !defined(__INTEL_COMPILER) // @raysan5, address PR #5367
unsigned long uln = 0;
_BitScanReverse(&uln, n);
return (int)(uln);

View file

@ -84,7 +84,6 @@ typedef struct {
// Global Variables Definition
//----------------------------------------------------------------------------------
extern CoreData CORE; // Global CORE state context
extern bool isGpuReady; // Flag to note GPU has been initialized successfully
static PlatformData platform = { 0 }; // Platform specific data
//----------------------------------------------------------------------------------
@ -268,6 +267,8 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd);
static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs
static GamepadButton AndroidTranslateGamepadButton(int button); // Map Android gamepad button to raylib gamepad button
static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform())
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
@ -358,13 +359,17 @@ void RestoreWindow(void)
// Set window configuration state using flags
void SetWindowState(unsigned int flags)
{
TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
// State change: FLAG_WINDOW_ALWAYS_RUN
if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
// Clear window configuration state flags
void ClearWindowState(unsigned int flags)
{
TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
// State change: FLAG_WINDOW_ALWAYS_RUN
if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
// Set icon for window
@ -602,7 +607,7 @@ void DisableCursor(void)
// Swap back buffer with front buffer (screen drawing)
void SwapScreenBuffer(void)
{
eglSwapBuffers(platform.device, platform.surface);
if (platform.surface != EGL_NO_SURFACE) eglSwapBuffers(platform.device, platform.surface);
}
//----------------------------------------------------------------------------------
@ -623,10 +628,9 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if you control the URL given.
// A user could craft a malicious string performing another action.
// Only call this function yourself not with user input or make sure to check the string yourself.
// Ref: https://github.com/raysan5/raylib/issues/686
// NOTE: This function is only safe to use if you control the URL given
// A user could craft a malicious string performing another action
// Only call this function yourself not with user input or make sure to check the string yourself
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code
@ -687,7 +691,7 @@ void SetMouseCursor(int cursor)
TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
}
// Get physical key name.
// Get physical key name
const char *GetKeyName(int key)
{
TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
@ -742,15 +746,15 @@ void PollInputEvents(void)
int pollEvents = 0;
// Poll Events (registered events) until we reach TIMEOUT which indicates there are no events left to poll
// NOTE: Activity is paused if not enabled (platform.appEnabled)
while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT))
// NOTE: Activity is paused if not enabled (platform.appEnabled) and always run flag is not set (FLAG_WINDOW_ALWAYS_RUN)
while ((pollResult = ALooper_pollOnce((platform.appEnabled || FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN))? 0 : -1, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT))
{
// Process this event
if (platform.source != NULL) platform.source->process(platform.app, platform.source);
// NOTE: Allow closing the window in case a configuration change happened.
// NOTE: Allow closing the window in case a configuration change happened
// The android_main function should be allowed to return to its caller in order for the
// Android OS to relaunch the activity.
// Android OS to relaunch the activity
if (platform.app->destroyRequested != 0)
{
CORE.Window.shouldClose = true;
@ -796,10 +800,10 @@ int InitPlatform(void)
//AConfiguration_getScreenLong(platform.app->config);
// Set some default window flags
CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false
CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false
CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true
CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // false
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // false
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // true
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // false
//----------------------------------------------------------------------------
// Initialize App command system
@ -829,13 +833,13 @@ int InitPlatform(void)
// Wait for window to be initialized (display and context)
while (!CORE.Window.ready)
{
// Process events until we reach TIMEOUT, which indicates no more events queued.
// Process events until we reach TIMEOUT, which indicates no more events queued
while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT))
{
// Process this event
if (platform.source != NULL) platform.source->process(platform.app, platform.source);
// NOTE: It's highly likely destroyRequested will never be non-zero at the start of the activity lifecycle.
// NOTE: It's highly likely destroyRequested will never be non-zero at the start of the activity lifecycle
//if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true;
}
}
@ -869,8 +873,9 @@ void ClosePlatform(void)
platform.device = EGL_NO_DISPLAY;
}
// NOTE: Reset global state in case the activity is being relaunched.
if (platform.app->destroyRequested != 0) {
// NOTE: Reset global state in case the activity is being relaunched
if (platform.app->destroyRequested != 0)
{
CORE = (CoreData){0};
platform = (PlatformData){0};
}
@ -882,12 +887,11 @@ void ClosePlatform(void)
// NOTE: returns false in case graphic device could not be created
static int InitGraphicsDevice(void)
{
CORE.Window.fullscreen = true;
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
EGLint samples = 0;
EGLint sampleBuffer = 0;
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
samples = 4;
sampleBuffer = 1;
@ -925,7 +929,7 @@ static int InitGraphicsDevice(void)
// Initialize the EGL device connection
if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
{
// If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
// If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred
TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
return -1;
}
@ -992,7 +996,7 @@ static int InitGraphicsDevice(void)
CORE.Window.ready = true;
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
return 0;
}
@ -1042,7 +1046,6 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Initialize OpenGL context (states and resources)
// NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
isGpuReady = true;
// Setup default viewport
// NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
@ -1059,7 +1062,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Set font white rectangle for shapes drawing, so shapes and text can be batched together
// WARNING: rshapes module is required, if not available, default internal white rectangle is used
Rectangle rec = GetFontDefault().recs[95];
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
// NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
@ -1081,41 +1084,26 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Initialize random seed
SetRandomSeed((unsigned int)time(NULL));
// TODO: GPU assets reload in case of lost focus (lost context)
// NOTE: This problem has been solved just unbinding and rebinding context from display
/*
if (assetsReloadRequired)
{
for (int i = 0; i < assetCount; i++)
{
// TODO: Unload old asset if required
// Load texture again to pointed texture
(*textureAsset + i) = LoadTexture(assetPath[i]);
}
}
*/
}
}
} break;
case APP_CMD_GAINED_FOCUS:
{
platform.appEnabled = true;
CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
//ResumeMusicStream();
} break;
case APP_CMD_PAUSE: break;
case APP_CMD_LOST_FOCUS:
{
platform.appEnabled = false;
CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
//PauseMusicStream();
} break;
case APP_CMD_TERM_WINDOW:
{
// Detach OpenGL context and destroy display surface
// NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming.
// NOTE 1: This case is used when the user exits the app without closing it, context is detached to ensure everything is recoverable upon resuming
// NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
// NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :(
if (platform.device != EGL_NO_DISPLAY)
@ -1179,16 +1167,16 @@ static GamepadButton AndroidTranslateGamepadButton(int button)
static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
{
// If additional inputs are required check:
// https://developer.android.com/ndk/reference/group/input
// https://developer.android.com/training/game-controllers/controller-input
// REF: https://developer.android.com/ndk/reference/group/input
// REF: https://developer.android.com/training/game-controllers/controller-input
int type = AInputEvent_getType(event);
int source = AInputEvent_getSource(event);
if (type == AINPUT_EVENT_TYPE_MOTION)
{
if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
if (FLAG_IS_SET(source, AINPUT_SOURCE_JOYSTICK) ||
FLAG_IS_SET(source, AINPUT_SOURCE_GAMEPAD))
{
// For now we'll assume a single gamepad which we "detect" on its input event
CORE.Input.Gamepad.ready[0] = true;
@ -1251,8 +1239,11 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
//int32_t AKeyEvent_getMetaState(event);
// Handle gamepad button presses and releases
if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
// NOTE: Skip gamepad handling if this is a keyboard event, as some devices
// report both AINPUT_SOURCE_KEYBOARD and AINPUT_SOURCE_GAMEPAD flags
if ((FLAG_IS_SET(source, AINPUT_SOURCE_JOYSTICK) ||
FLAG_IS_SET(source, AINPUT_SOURCE_GAMEPAD)) &&
!FLAG_IS_SET(source, AINPUT_SOURCE_KEYBOARD))
{
// For now we'll assume a single gamepad which we "detect" on its input event
CORE.Input.Gamepad.ready[0] = true;
@ -1290,7 +1281,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
{
// Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS
// Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS
// It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected.
// It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected
// NOTE: AndroidManifest.xml must have <activity android:configChanges="orientation|keyboardHidden|screenSize" >
// Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
return 0;
@ -1419,15 +1410,9 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
// Stores the previous position of touch[0] only while it's active to calculate the delta.
if (flags == AMOTION_EVENT_ACTION_MOVE)
{
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
}
else
{
CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
}
// Stores the previous position of touch[0] only while it's active to calculate the delta
if (flags == AMOTION_EVENT_ACTION_MOVE) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
else CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
// Map touch[0] as mouse input for convenience
CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0];
@ -1436,4 +1421,82 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
return 0;
}
// Compute framebuffer size relative to screen size and display size
// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
static void SetupFramebuffer(int width, int height)
{
// Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
{
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);
// Downscaling to fit display with border-bars
float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
if (widthRatio <= heightRatio)
{
CORE.Window.render.width = CORE.Window.display.width;
CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
CORE.Window.renderOffset.x = 0;
CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
}
else
{
CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
CORE.Window.render.height = CORE.Window.display.height;
CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
CORE.Window.renderOffset.y = 0;
}
// Screen scaling required
float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
// NOTE: We render to full display resolution!
// We just need to calculate above parameters for downscale matrix and offsets
CORE.Window.render.width = CORE.Window.display.width;
CORE.Window.render.height = CORE.Window.display.height;
TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
}
else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
{
// Required screen size is smaller than display size
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);
if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
{
CORE.Window.screen.width = CORE.Window.display.width;
CORE.Window.screen.height = CORE.Window.display.height;
}
// Upscaling to fit display with border-bars
float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
if (displayRatio <= screenRatio)
{
CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
CORE.Window.renderOffset.x = 0;
CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
}
else
{
CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
CORE.Window.render.height = CORE.Window.screen.height;
CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
CORE.Window.renderOffset.y = 0;
}
}
else
{
CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = CORE.Window.screen.height;
CORE.Window.renderOffset.x = 0;
CORE.Window.renderOffset.y = 0;
}
}
// EOF

File diff suppressed because it is too large Load diff

View file

@ -48,11 +48,6 @@
*
**********************************************************************************************/
#ifndef RAYLIB_H /* this should never actually happen, it's only here for IDEs */
#include "raylib.h"
#include "../rcore.c"
#endif
#if defined(PLATFORM_WEB_RGFW)
#define RGFW_NO_GL_HEADER
#endif
@ -295,23 +290,21 @@ bool WindowShouldClose(void)
// Toggle fullscreen mode
void ToggleFullscreen(void)
{
if (!CORE.Window.fullscreen)
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
// Store previous window position (in case we exit fullscreen)
CORE.Window.previousPosition = CORE.Window.position;
CORE.Window.previousScreen = CORE.Window.screen;
platform.mon = RGFW_window_getMonitor(platform.window);
CORE.Window.fullscreen = true;
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
RGFW_monitor_scaleToWindow(platform.mon, platform.window);
RGFW_window_setFullscreen(platform.window, 1);
}
else
{
CORE.Window.fullscreen = false;
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
if (platform.mon.mode.area.w)
{
@ -330,13 +323,15 @@ void ToggleFullscreen(void)
// Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
// NOTE: V-Sync can be enabled by graphic driver configuration
if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1);
}
// Toggle borderless windowed mode
void ToggleBorderlessWindowed(void)
{
if (CORE.Window.fullscreen)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) ToggleFullscreen();
if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
CORE.Window.previousPosition = CORE.Window.position;
CORE.Window.previousScreen = CORE.Window.screen;
@ -353,8 +348,6 @@ void ToggleBorderlessWindowed(void)
CORE.Window.position = CORE.Window.previousPosition;
RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height));
}
CORE.Window.fullscreen = !CORE.Window.fullscreen;
}
// Set window state: maximized, if resizable
@ -372,7 +365,7 @@ void MinimizeWindow(void)
// Restore window from being minimized/maximized
void RestoreWindow(void)
{
if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
RGFW_window_restore(platform.window);
}
@ -382,72 +375,72 @@ void SetWindowState(unsigned int flags)
{
if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
CORE.Window.flags |= flags;
FLAG_SET(CORE.Window.flags, flags);
if (flags & FLAG_VSYNC_HINT)
if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
RGFW_window_swapInterval(platform.window, 1);
}
if (flags & FLAG_FULLSCREEN_MODE)
if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
if (!CORE.Window.fullscreen) ToggleFullscreen();
ToggleFullscreen();
}
if (flags & FLAG_WINDOW_RESIZABLE)
if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0));
RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0));
}
if (flags & FLAG_WINDOW_UNDECORATED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
RGFW_window_setBorder(platform.window, 0);
}
if (flags & FLAG_WINDOW_HIDDEN)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
RGFW_window_hide(platform.window);
}
if (flags & FLAG_WINDOW_MINIMIZED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
RGFW_window_minimize(platform.window);
}
if (flags & FLAG_WINDOW_MAXIMIZED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
{
RGFW_window_maximize(platform.window);
}
if (flags & FLAG_WINDOW_UNFOCUSED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
platform.window->_flags &= ~RGFW_windowFocusOnShow;
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
FLAG_CLEAR(platform.window->_flags, RGFW_windowFocusOnShow);
RGFW_window_setFlags(platform.window, platform.window->_flags);
}
if (flags & FLAG_WINDOW_TOPMOST)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
RGFW_window_setFloating(platform.window, RGFW_TRUE);
}
if (flags & FLAG_WINDOW_ALWAYS_RUN)
if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
{
CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
if (flags & FLAG_WINDOW_TRANSPARENT)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
}
if (flags & FLAG_WINDOW_HIGHDPI)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
}
if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
RGFW_window_setMousePassthrough(platform.window, 1);
}
if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
ToggleBorderlessWindowed();
}
if (flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
RGFW_setGLHint(RGFW_glSamples, 4);
}
if (flags & FLAG_INTERLACED_HINT)
if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
}
@ -456,122 +449,86 @@ void SetWindowState(unsigned int flags)
// Clear window configuration state flags
void ClearWindowState(unsigned int flags)
{
CORE.Window.flags &= ~flags;
FLAG_CLEAR(CORE.Window.flags, flags);
if (flags & FLAG_VSYNC_HINT)
if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
RGFW_window_swapInterval(platform.window, 0);
}
if (flags & FLAG_FULLSCREEN_MODE)
if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
if (CORE.Window.fullscreen) ToggleFullscreen();
ToggleFullscreen();
}
if (flags & FLAG_WINDOW_RESIZABLE)
if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
}
if (flags & FLAG_WINDOW_UNDECORATED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
RGFW_window_setBorder(platform.window, 1);
}
if (flags & FLAG_WINDOW_HIDDEN)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
RGFW_window_show(platform.window);
}
if (flags & FLAG_WINDOW_MINIMIZED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
RGFW_window_restore(platform.window);
}
if (flags & FLAG_WINDOW_MAXIMIZED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
{
if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
RGFW_window_restore(platform.window);
}
if (flags & FLAG_WINDOW_UNFOCUSED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
RGFW_window_setFlags(platform.window, platform.window->_flags | RGFW_windowFocusOnShow);
CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
}
if (flags & FLAG_WINDOW_TOPMOST)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
RGFW_window_setFloating(platform.window, RGFW_FALSE);
}
if (flags & FLAG_WINDOW_ALWAYS_RUN)
{
CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
}
if (flags & FLAG_WINDOW_TRANSPARENT)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
}
if (flags & FLAG_WINDOW_HIGHDPI)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
}
if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
RGFW_window_setMousePassthrough(platform.window, 0);
}
if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
if (CORE.Window.fullscreen) ToggleBorderlessWindowed();
ToggleBorderlessWindowed();
}
if (flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
RGFW_setGLHint(RGFW_glSamples, 0);
}
if (flags & FLAG_INTERLACED_HINT)
if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
}
}
int RGFW_formatToChannels(int format)
{
switch (format)
{
case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
case PIXELFORMAT_UNCOMPRESSED_R16: // 16 bpp (1 channel - half float)
case PIXELFORMAT_UNCOMPRESSED_R32: // 32 bpp (1 channel - float)
return 1;
case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: // 8*2 bpp (2 channels)
case PIXELFORMAT_UNCOMPRESSED_R5G6B5: // 16 bpp
case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // 24 bpp
case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: // 16 bpp (1 bit alpha)
case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: // 16 bpp (4 bit alpha)
case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: // 32 bpp
return 2;
case PIXELFORMAT_UNCOMPRESSED_R32G32B32: // 32*3 bpp (3 channels - float)
case PIXELFORMAT_UNCOMPRESSED_R16G16B16: // 16*3 bpp (3 channels - half float)
case PIXELFORMAT_COMPRESSED_DXT1_RGB: // 4 bpp (no alpha)
case PIXELFORMAT_COMPRESSED_ETC1_RGB: // 4 bpp
case PIXELFORMAT_COMPRESSED_ETC2_RGB: // 4 bpp
case PIXELFORMAT_COMPRESSED_PVRT_RGB: // 4 bpp
return 3;
case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: // 32*4 bpp (4 channels - float)
case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: // 16*4 bpp (4 channels - half float)
case PIXELFORMAT_COMPRESSED_DXT1_RGBA: // 4 bpp (1 bit alpha)
case PIXELFORMAT_COMPRESSED_DXT3_RGBA: // 8 bpp
case PIXELFORMAT_COMPRESSED_DXT5_RGBA: // 8 bpp
case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: // 8 bpp
case PIXELFORMAT_COMPRESSED_PVRT_RGBA: // 4 bpp
case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: // 8 bpp
case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 2 bpp
return 4;
default: return 4;
}
}
// Set icon for window
void SetWindowIcon(Image image)
{
RGFW_window_setIcon(platform.window, (u8 *)image.data, RGFW_AREA(image.width, image.height), RGFW_formatToChannels(image.format));
if (image.format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
{
TRACELOG(LOG_WARNING, "RGFW: Window icon image must be in R8G8B8A8 pixel format");
return;
}
RGFW_window_setIcon(platform.window, (u8 *)image.data, RGFW_AREA(image.width, image.height), 4);
}
// Set icon for window
@ -588,12 +545,17 @@ void SetWindowIcons(Image *images, int count)
for (int i = 0; i < count; i++)
{
if (images[i].format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
{
TRACELOG(LOG_WARNING, "RGFW: Window icon image must be in R8G8B8A8 pixel format");
continue;
}
if ((bigIcon == NULL) || ((images[i].width > bigIcon->width) && (images[i].height > bigIcon->height))) bigIcon = &images[i];
if ((smallIcon == NULL) || ((images[i].width < smallIcon->width) && (images[i].height > smallIcon->height))) smallIcon = &images[i];
}
if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, RGFW_AREA(smallIcon->width, smallIcon->height), RGFW_formatToChannels(smallIcon->format), RGFW_iconWindow);
if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, RGFW_AREA(bigIcon->width, bigIcon->height), RGFW_formatToChannels(bigIcon->format), RGFW_iconTaskbar);
if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, RGFW_AREA(smallIcon->width, smallIcon->height), 4, RGFW_iconWindow);
if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, RGFW_AREA(bigIcon->width, bigIcon->height), 4, RGFW_iconTaskbar);
}
}
@ -843,8 +805,9 @@ void EnableCursor(void)
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
RGFW_window_showMouse(platform.window, true);
CORE.Input.Mouse.cursorHidden = false;
ShowCursor();
CORE.Input.Mouse.cursorLocked = true;
}
// Disables cursor (lock cursor)
@ -853,6 +816,8 @@ void DisableCursor(void)
RGFW_disableCursor = true;
RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0));
HideCursor();
CORE.Input.Mouse.cursorLocked = true;
}
// Swap back buffer with front buffer (screen drawing)
@ -872,17 +837,27 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if you control the URL given.
// A user could craft a malicious string performing another action.
// Only call this function yourself not with user input or make sure to check the string yourself.
// Ref: https://github.com/raysan5/raylib/issues/686
// NOTE: This function is only safe to use if you control the URL given
// A user could craft a malicious string performing another action
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code on target platform
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
{
// TODO: Open URL implementation
char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
#if defined(_WIN32)
sprintf(cmd, "explorer \"%s\"", url);
#endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
#endif
#if defined(__APPLE__)
sprintf(cmd, "open '%s'", url);
#endif
int result = system(cmd);
if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
RL_FREE(cmd);
}
}
@ -917,7 +892,7 @@ void SetMouseCursor(int cursor)
RGFW_window_setMouseStandard(platform.window, cursor);
}
// Get physical key name.
// Get physical key name
const char *GetKeyName(int key)
{
TRACELOG(LOG_WARNING, "GetKeyName() unsupported on target platform");
@ -983,7 +958,7 @@ void PollInputEvents(void)
CORE.Window.resizedLastFrame = false;
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
if (platform.window->_flags & RGFW_HOLD_MOUSE)
if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE))
{
CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
@ -1062,18 +1037,18 @@ void PollInputEvents(void)
} break;
case RGFW_windowMaximized:
{
CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // The window was maximized
} break;
case RGFW_windowMinimized:
{
CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was iconified
} break;
case RGFW_windowRestored:
{
if (RGFW_window_isMaximized(platform.window))
CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // The window was restored
if (RGFW_window_isMinimized(platform.window))
CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was restored
} break;
case RGFW_windowMoved:
{
@ -1097,11 +1072,7 @@ void PollInputEvents(void)
CORE.Input.Keyboard.currentKeyState[key] = 1;
}
// TODO: Put exitKey verification outside the switch?
if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey])
{
CORE.Window.shouldClose = true;
}
if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) CORE.Window.shouldClose = true;
// NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
// Check if there is space available in the queue
@ -1159,7 +1130,7 @@ void PollInputEvents(void)
} break;
case RGFW_mousePosChanged:
{
if (platform.window->_flags & RGFW_HOLD_MOUSE)
if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE))
{
CORE.Input.Mouse.currentPosition.x += (float)event->vector.x;
CORE.Input.Mouse.currentPosition.y += (float)event->vector.y;
@ -1283,24 +1254,22 @@ int InitPlatform(void)
unsigned int flags = RGFW_windowCenter | RGFW_windowAllowDND;
// Check window creation flags
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
CORE.Window.fullscreen = true;
flags |= RGFW_windowFullscreen;
FLAG_SET(flags, RGFW_windowFullscreen);
}
if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
CORE.Window.fullscreen = true;
flags |= RGFW_windowedFullscreen;
FLAG_SET(flags, RGFW_windowedFullscreen);
}
if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= RGFW_windowNoBorder;
if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) flags |= RGFW_windowNoResize;
if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= RGFW_windowTransparent;
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) flags |= RGFW_windowFullscreen;
if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) flags |= RGFW_windowHide;
if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= RGFW_windowMaximize;
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) FLAG_SET(flags, RGFW_windowNoBorder);
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) FLAG_SET(flags, RGFW_windowNoResize);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) FLAG_SET(flags, RGFW_windowTransparent);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) FLAG_SET(flags, RGFW_windowFullscreen);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_SET(flags, RGFW_windowHide);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(flags, RGFW_windowMaximize);
// NOTE: Some OpenGL context attributes must be set before window creation
// Check selection OpenGL version
@ -1320,9 +1289,9 @@ int InitPlatform(void)
RGFW_setGLHint(RGFW_glMinor, 3);
}
if (CORE.Window.flags & FLAG_MSAA_4X_HINT) RGFW_setGLHint(RGFW_glSamples, 4);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) RGFW_setGLHint(RGFW_glSamples, 4);
if (!(CORE.Window.flags & FLAG_WINDOW_UNFOCUSED)) flags |= RGFW_windowFocusOnShow | RGFW_windowFocus;
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_SET(flags, RGFW_windowFocusOnShow | RGFW_windowFocus);
platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags);
platform.mon.mode.area.w = 0;
@ -1342,11 +1311,7 @@ int InitPlatform(void)
CORE.Window.display.width = CORE.Window.screen.width;
CORE.Window.display.height = CORE.Window.screen.height;
#endif
// TODO: Is this needed by raylib now?
// If so, rcore_desktop_sdl should be updated too
//SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1);
RGFW_window_makeCurrent(platform.window);
// Check surface and context activation

View file

@ -57,7 +57,7 @@
// SDL base library (window/rendered, input, timing... functionality)
#ifdef USING_SDL3_PROJECT
#include "SDL3/SDL.h"
#elif USING_SDL2_PROJECT
#elif defined(USING_SDL2_PROJECT)
#include "SDL2/SDL.h"
#else
#include "SDL.h"
@ -71,7 +71,7 @@
// SDL OpenGL functionality (if required, instead of internal renderer)
#ifdef USING_SDL3_PROJECT
#include "SDL3/SDL_opengl.h"
#elif USING_SDL2_PROJECT
#elif defined(USING_SDL2_PROJECT)
#include "SDL2/SDL_opengl.h"
#else
#include "SDL_opengl.h"
@ -463,22 +463,20 @@ void ToggleFullscreen(void)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
{
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
SDL_SetWindowFullscreen(platform.window, 0);
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
CORE.Window.fullscreen = false;
FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
else
{
SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN);
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
CORE.Window.fullscreen = true;
FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
@ -491,20 +489,20 @@ void ToggleBorderlessWindowed(void)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
{
if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
SDL_SetWindowFullscreen(platform.window, 0);
CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
else
{
SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
@ -514,14 +512,14 @@ void ToggleBorderlessWindowed(void)
void MaximizeWindow(void)
{
SDL_MaximizeWindow(platform.window);
if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
// Set window state: minimized
void MinimizeWindow(void)
{
SDL_MinimizeWindow(platform.window);
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
}
// Restore window from being minimized/maximized
@ -536,83 +534,83 @@ void SetWindowState(unsigned int flags)
{
if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
CORE.Window.flags |= flags;
FLAG_SET(CORE.Window.flags, flags);
if (flags & FLAG_VSYNC_HINT)
if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
SDL_GL_SetSwapInterval(1);
}
if (flags & FLAG_FULLSCREEN_MODE)
if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
const int monitor = SDL_GetWindowDisplayIndex(platform.window);
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
{
SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN);
CORE.Window.fullscreen = true;
FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
}
if (flags & FLAG_WINDOW_RESIZABLE)
if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
SDL_SetWindowResizable(platform.window, SDL_TRUE);
}
if (flags & FLAG_WINDOW_UNDECORATED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
SDL_SetWindowBordered(platform.window, SDL_FALSE);
}
if (flags & FLAG_WINDOW_HIDDEN)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
SDL_HideWindow(platform.window);
}
if (flags & FLAG_WINDOW_MINIMIZED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
SDL_MinimizeWindow(platform.window);
}
if (flags & FLAG_WINDOW_MAXIMIZED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
{
SDL_MaximizeWindow(platform.window);
}
if (flags & FLAG_WINDOW_UNFOCUSED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
// NOTE: To be able to implement this part it seems that we should
// do it ourselves, via 'windows.h', 'X11/Xlib.h' or even 'Cocoa.h'
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
}
if (flags & FLAG_WINDOW_TOPMOST)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE);
}
if (flags & FLAG_WINDOW_ALWAYS_RUN)
if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
{
CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
}
if (flags & FLAG_WINDOW_TRANSPARENT)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL");
}
if (flags & FLAG_WINDOW_HIGHDPI)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
// NOTE: Such a function does not seem to exist
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL");
}
if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
//SDL_SetWindowGrab(platform.window, SDL_FALSE);
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL");
}
if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
const int monitor = SDL_GetWindowDisplayIndex(platform.window);
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@ -621,12 +619,12 @@ void SetWindowState(unsigned int flags)
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
}
if (flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable multisampling buffers
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); // Enable multisampling
}
if (flags & FLAG_INTERLACED_HINT)
if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL");
}
@ -635,74 +633,69 @@ void SetWindowState(unsigned int flags)
// Clear window configuration state flags
void ClearWindowState(unsigned int flags)
{
CORE.Window.flags &= ~flags;
FLAG_CLEAR(CORE.Window.flags, flags);
if (flags & FLAG_VSYNC_HINT)
if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
SDL_GL_SetSwapInterval(0);
}
if (flags & FLAG_FULLSCREEN_MODE)
if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
SDL_SetWindowFullscreen(platform.window, 0);
CORE.Window.fullscreen = false;
}
if (flags & FLAG_WINDOW_RESIZABLE)
if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
{
SDL_SetWindowResizable(platform.window, SDL_FALSE);
}
if (flags & FLAG_WINDOW_UNDECORATED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
SDL_SetWindowBordered(platform.window, SDL_TRUE);
}
if (flags & FLAG_WINDOW_HIDDEN)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
SDL_ShowWindow(platform.window);
}
if (flags & FLAG_WINDOW_MINIMIZED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
SDL_RestoreWindow(platform.window);
}
if (flags & FLAG_WINDOW_MAXIMIZED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
{
SDL_RestoreWindow(platform.window);
}
if (flags & FLAG_WINDOW_UNFOCUSED)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
//SDL_RaiseWindow(platform.window);
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
}
if (flags & FLAG_WINDOW_TOPMOST)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE);
}
if (flags & FLAG_WINDOW_ALWAYS_RUN)
{
CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
}
if (flags & FLAG_WINDOW_TRANSPARENT)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL");
}
if (flags & FLAG_WINDOW_HIGHDPI)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
// NOTE: There also doesn't seem to be a feature to disable high DPI once enabled
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL");
}
if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
//SDL_SetWindowGrab(platform.window, SDL_TRUE);
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL");
}
if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
SDL_SetWindowFullscreen(platform.window, 0);
}
if (flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); // Disable multisampling buffers
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); // Disable multisampling
}
if (flags & FLAG_INTERLACED_HINT)
if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL");
}
@ -838,7 +831,7 @@ void SetWindowMonitor(int monitor)
{
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@ -847,7 +840,7 @@ void SetWindowMonitor(int monitor)
// 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3,
// see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba
// 2. A workaround for SDL2 is leaving fullscreen, moving the window, then entering full screen again
const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)? true : false;
const bool wasFullscreen = (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))? true : false;
const int screenWidth = CORE.Window.screen.width;
const int screenHeight = CORE.Window.screen.height;
@ -966,7 +959,7 @@ Vector2 GetMonitorPosition(int monitor)
{
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@ -994,7 +987,7 @@ int GetMonitorWidth(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@ -1015,7 +1008,7 @@ int GetMonitorHeight(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@ -1036,7 +1029,7 @@ int GetMonitorPhysicalWidth(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@ -1046,7 +1039,7 @@ int GetMonitorPhysicalWidth(int monitor)
SDL_DisplayMode mode;
SDL_GetCurrentDisplayMode(monitor, &mode);
// Calculate size on inches, then convert to millimeter
if (ddpi > 0.0f) width = (mode.w/ddpi)*25.4f;
if (ddpi > 0.0f) width = (int)((mode.w/ddpi)*25.4f);
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
@ -1060,7 +1053,7 @@ int GetMonitorPhysicalHeight(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@ -1070,7 +1063,7 @@ int GetMonitorPhysicalHeight(int monitor)
SDL_DisplayMode mode;
SDL_GetCurrentDisplayMode(monitor, &mode);
// Calculate size on inches, then convert to millimeter
if (ddpi > 0.0f) height = (mode.h/ddpi)*25.4f;
if (ddpi > 0.0f) height = (int)((mode.h/ddpi)*25.4f);
}
else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
@ -1084,7 +1077,7 @@ int GetMonitorRefreshRate(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@ -1104,7 +1097,7 @@ const char *GetMonitorName(int monitor)
const int monitorCount = SDL_GetNumVideoDisplays();
#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
if ((monitor > 0) && (monitor <= monitorCount))
if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
#else
if ((monitor >= 0) && (monitor < monitorCount))
#endif
@ -1132,14 +1125,15 @@ Vector2 GetWindowScaleDPI(void)
{
Vector2 scale = { 1.0f, 1.0f };
#ifndef USING_VERSION_SDL3
// NOTE: SDL_GetWindowDisplayScale was only added on SDL3
// see https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale
// TODO: Implement the window scale factor calculation manually
TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
#else
#if defined(USING_VERSION_SDL3)
// NOTE: SDL_GetWindowDisplayScale added on SDL3
// REF: https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale
scale.x = SDL_GetWindowDisplayScale(platform.window);
scale.y = scale.x;
#else
// NOTE: SDL_GetWindowDisplayScale not available on SDL2
// TODO: Implement the window scale factor calculation manually
TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
#endif
return scale;
@ -1193,14 +1187,14 @@ Image GetClipboardImage(void)
size_t dataSize = 0;
void *fileData = NULL;
for (int i = 0; i < SDL_arraysize(imageFormats); ++i)
for (int i = 0; i < SDL_arraysize(imageFormats); i++)
{
// NOTE: This pointer should be free with SDL_free() at some point
fileData = SDL_GetClipboardData(imageFormats[i], &dataSize);
if (fileData)
{
image = LoadImageFromMemory(imageExtensions[i], fileData, dataSize);
image = LoadImageFromMemory(imageExtensions[i], fileData, (int)dataSize);
if (IsImageValid(image))
{
TRACELOG(LOG_INFO, "Clipboard: Got image from clipboard successfully: %s", imageExtensions[i]);
@ -1242,14 +1236,7 @@ void EnableCursor(void)
{
SDL_SetRelativeMouseMode(SDL_FALSE);
#if defined(USING_VERSION_SDL3)
// NOTE: SDL_ShowCursor() has been split into three functions:
// SDL_ShowCursor(), SDL_HideCursor(), and SDL_CursorVisible()
SDL_ShowCursor();
#else
SDL_ShowCursor(SDL_ENABLE);
#endif
ShowCursor();
CORE.Input.Mouse.cursorLocked = false;
}
@ -1258,6 +1245,7 @@ void DisableCursor(void)
{
SDL_SetRelativeMouseMode(SDL_TRUE);
HideCursor();
CORE.Input.Mouse.cursorLocked = true;
}
@ -1290,7 +1278,7 @@ double GetTime(void)
// NOTE: This function is only safe to use if you control the URL given
// A user could craft a malicious string performing another action
// Only call this function yourself not with user input or make sure to check the string yourself
// Ref: https://github.com/raysan5/raylib/issues/686
// REF: https://github.com/raysan5/raylib/issues/686
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code
@ -1406,11 +1394,11 @@ void PollInputEvents(void)
//-----------------------------------------------------------------------------
// WARNING: Indexes into this array are obtained by using SDL_Scancode values, not SDL_Keycode values
//const Uint8 *keys = SDL_GetKeyboardState(NULL);
//for (int i = 0; i < 256; ++i) CORE.Input.Keyboard.currentKeyState[i] = keys[i];
//for (int i = 0; i < 256; i++) CORE.Input.Keyboard.currentKeyState[i] = keys[i];
CORE.Window.resizedLastFrame = false;
if ((CORE.Window.eventWaiting) || (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) == 0)))
if ((CORE.Window.eventWaiting) || (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED) && !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN)))
{
SDL_WaitEvent(NULL);
CORE.Time.previous = GetTime();
@ -1436,11 +1424,13 @@ void PollInputEvents(void)
CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
#if defined(USING_VERSION_SDL3)
// const char *data; /**< The text for SDL_EVENT_DROP_TEXT and the file name for SDL_EVENT_DROP_FILE, NULL for other events */
// Event memory is now managed by SDL, so you should not free the data in SDL_EVENT_DROP_FILE, and if you want to hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events, you should make a copy of it. SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed.
strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data);
// const char *data; // The text for SDL_EVENT_DROP_TEXT and the file name for SDL_EVENT_DROP_FILE, NULL for other events
// Event memory is now managed by SDL, so you should not free the data in SDL_EVENT_DROP_FILE,
// and if you want to hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events,
// you should make a copy of it. SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed
strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1);
#else
strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file);
strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file, MAX_FILEPATH_LENGTH - 1);
SDL_free(event.drop.file);
#endif
@ -1451,9 +1441,9 @@ void PollInputEvents(void)
CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
#if defined(USING_VERSION_SDL3)
strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data);
strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1);
#else
strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file);
strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file, MAX_FILEPATH_LENGTH - 1);
SDL_free(event.drop.file);
#endif
@ -1463,13 +1453,13 @@ void PollInputEvents(void)
} break;
// Window events are also polled (Minimized, maximized, close...)
// Window events are also polled (minimized, maximized, close...)
#ifndef USING_VERSION_SDL3
// SDL3 states:
// The SDL_WINDOWEVENT_* events have been moved to top level events, and SDL_WINDOWEVENT has been removed
// In general, handling this change just means checking for the individual events instead of first checking for SDL_WINDOWEVENT
// and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event.
// and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event
case SDL_WINDOWEVENT:
{
switch (event.window.event)
@ -1482,7 +1472,7 @@ void PollInputEvents(void)
const int height = event.window.data2;
SetupViewport(width, height);
// if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale
if (IsWindowState(FLAG_WINDOW_HIGHDPI))
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
CORE.Window.screen.width = (int)(width/GetWindowScaleDPI().x);
CORE.Window.screen.height = (int)(height/GetWindowScaleDPI().y);
@ -1497,8 +1487,9 @@ void PollInputEvents(void)
CORE.Window.resizedLastFrame = true;
#ifndef USING_VERSION_SDL3
// Manually detect if the window was maximized (due to SDL2 restore being unreliable on some platforms) to remove the FLAG_WINDOW_MAXIMIZED accordingly
if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0)
// Manually detect if the window was maximized (due to SDL2 restore being unreliable on some platforms)
// to remove the FLAG_WINDOW_MAXIMIZED accordingly
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED))
{
int borderTop = 0;
int borderLeft = 0;
@ -1508,59 +1499,53 @@ void PollInputEvents(void)
SDL_Rect usableBounds;
SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(platform.window), &usableBounds);
if ((width + borderLeft + borderRight != usableBounds.w) && (height + borderTop + borderBottom != usableBounds.h)) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
if ((width + borderLeft + borderRight != usableBounds.w) && (height + borderTop + borderBottom != usableBounds.h)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
#endif
} break;
case SDL_WINDOWEVENT_ENTER:
{
CORE.Input.Mouse.cursorOnScreen = true;
} break;
case SDL_WINDOWEVENT_LEAVE:
{
CORE.Input.Mouse.cursorOnScreen = false;
} break;
case SDL_WINDOWEVENT_ENTER: CORE.Input.Mouse.cursorOnScreen = true; break;
case SDL_WINDOWEVENT_LEAVE: CORE.Input.Mouse.cursorOnScreen = false; break;
case SDL_WINDOWEVENT_MINIMIZED:
{
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
} break;
case SDL_WINDOWEVENT_MAXIMIZED:
{
if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) == 0) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
} break;
case SDL_WINDOWEVENT_RESTORED:
{
if ((SDL_GetWindowFlags(platform.window) & SDL_WINDOW_MINIMIZED) == 0)
if (!FLAG_IS_SET(SDL_GetWindowFlags(platform.window), SDL_WINDOW_MINIMIZED))
{
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
}
#ifdef USING_VERSION_SDL3
if ((SDL_GetWindowFlags(platform.window) & SDL_WINDOW_MAXIMIZED) == 0)
if (!FLAG_IS_SET(SDL_GetWindowFlags(platform.window), SDL_WINDOW_MAXIMIZED))
{
if ((CORE.Window.flags & SDL_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~SDL_WINDOW_MAXIMIZED;
if (FLAG_IS_SET(CORE.Window.flags, SDL_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, SDL_WINDOW_MAXIMIZED);
}
#endif
} break;
case SDL_WINDOWEVENT_HIDDEN:
{
if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) CORE.Window.flags |= FLAG_WINDOW_HIDDEN;
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
} break;
case SDL_WINDOWEVENT_SHOWN:
{
if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
} break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
{
if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
} break;
case SDL_WINDOWEVENT_FOCUS_LOST:
{
if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0) CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
} break;
#ifndef USING_VERSION_SDL3
@ -1573,7 +1558,7 @@ void PollInputEvents(void)
case SDL_KEYDOWN:
{
#if defined(USING_VERSION_SDL3)
// SDL3 Migration: The following structures have been removed: * SDL_Keysym
// SDL3 Migration: The following structures have been removed: SDL_Keysym
KeyboardKey key = ConvertScancodeToKey(event.key.scancode);
#else
KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode);
@ -1593,11 +1578,9 @@ void PollInputEvents(void)
if (event.key.repeat) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
// TODO: Put exitKey verification outside the switch?
if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey])
{
CORE.Window.shouldClose = true;
}
// Check for registered exit key to request exit game loop on next iteration
if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) CORE.Window.shouldClose = true;
} break;
case SDL_KEYUP:
@ -1708,7 +1691,7 @@ void PollInputEvents(void)
int jid = event.jdevice.which; // Joystick device index
// check if already added at InitPlatform
for (int i = 0; i < MAX_GAMEPADS; ++i)
for (int i = 0; i < MAX_GAMEPADS; i++)
{
if (jid == platform.gamepadId[i])
{
@ -1734,12 +1717,11 @@ void PollInputEvents(void)
CORE.Input.Gamepad.axisState[nextAvailableSlot][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
CORE.Input.Gamepad.axisState[nextAvailableSlot][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
memset(CORE.Input.Gamepad.name[nextAvailableSlot], 0, MAX_GAMEPAD_NAME_LENGTH);
strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], SDL_GameControllerNameForIndex(nextAvailableSlot), MAX_GAMEPAD_NAME_LENGTH - 1);
}
else
{
TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
const char *controllerName = SDL_GameControllerNameForIndex(nextAvailableSlot);
if (controllerName != NULL) strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], controllerName, MAX_GAMEPAD_NAME_LENGTH - 1);
else strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], "noname", 6);
}
else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
}
} break;
case SDL_JOYDEVICEREMOVED:
@ -1762,7 +1744,11 @@ void PollInputEvents(void)
{
int button = -1;
#if defined(USING_VERSION_SDL3)
switch (event.gbutton.button)
#else
switch (event.jbutton.button)
#endif
{
case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
@ -1790,7 +1776,11 @@ void PollInputEvents(void)
{
for (int i = 0; i < MAX_GAMEPADS; i++)
{
#if defined(USING_VERSION_SDL3)
if (platform.gamepadId[i] == event.gbutton.which)
#else
if (platform.gamepadId[i] == event.jbutton.which)
#endif
{
CORE.Input.Gamepad.currentButtonState[i][button] = 1;
CORE.Input.Gamepad.lastButtonPressed = button;
@ -1803,7 +1793,11 @@ void PollInputEvents(void)
{
int button = -1;
#if defined(USING_VERSION_SDL3)
switch (event.gbutton.button)
#else
switch (event.jbutton.button)
#endif
{
case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
@ -1831,7 +1825,11 @@ void PollInputEvents(void)
{
for (int i = 0; i < MAX_GAMEPADS; i++)
{
#if defined(USING_VERSION_SDL3)
if (platform.gamepadId[i] == event.gbutton.which)
#else
if (platform.gamepadId[i] == event.jbutton.which)
#endif
{
CORE.Input.Gamepad.currentButtonState[i][button] = 0;
if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
@ -1930,38 +1928,30 @@ int InitPlatform(void)
// Initialize graphic device: display/window and graphic context
//----------------------------------------------------------------------------
unsigned int flags = 0;
flags |= SDL_WINDOW_SHOWN;
flags |= SDL_WINDOW_INPUT_FOCUS;
flags |= SDL_WINDOW_MOUSE_FOCUS;
flags |= SDL_WINDOW_MOUSE_CAPTURE; // Window has mouse captured
FLAG_SET(flags, SDL_WINDOW_SHOWN);
FLAG_SET(flags, SDL_WINDOW_INPUT_FOCUS);
FLAG_SET(flags, SDL_WINDOW_MOUSE_FOCUS);
FLAG_SET(flags, SDL_WINDOW_MOUSE_CAPTURE); // Window has mouse captured
// Check window creation flags
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) FLAG_SET(flags, SDL_WINDOW_FULLSCREEN);
//if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_SET(flags, SDL_WINDOW_HIDDEN);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) FLAG_SET(flags, SDL_WINDOW_BORDERLESS);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) FLAG_SET(flags, SDL_WINDOW_RESIZABLE);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_SET(flags, SDL_WINDOW_MINIMIZED);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(flags, SDL_WINDOW_MAXIMIZED);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED))
{
CORE.Window.fullscreen = true;
flags |= SDL_WINDOW_FULLSCREEN;
FLAG_CLEAR(flags, SDL_WINDOW_INPUT_FOCUS);
FLAG_CLEAR(flags, SDL_WINDOW_MOUSE_FOCUS);
}
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) FLAG_SET(flags, SDL_WINDOW_ALWAYS_ON_TOP);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) FLAG_CLEAR(flags, SDL_WINDOW_MOUSE_CAPTURE);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) FLAG_SET(flags, SDL_WINDOW_ALLOW_HIGHDPI);
//if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) flags |= SDL_WINDOW_HIDDEN;
if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= SDL_WINDOW_BORDERLESS;
if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) flags |= SDL_WINDOW_RESIZABLE;
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) flags |= SDL_WINDOW_MINIMIZED;
if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= SDL_WINDOW_MAXIMIZED;
if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0)
{
flags &= ~SDL_WINDOW_INPUT_FOCUS;
flags &= ~SDL_WINDOW_MOUSE_FOCUS;
}
if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) flags |= SDL_WINDOW_ALWAYS_ON_TOP;
if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) flags |= SDL_WINDOW_ALLOW_HIGHDPI;
//if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= SDL_WINDOW_TRANSPARENT; // Alternative: SDL_GL_ALPHA_SIZE = 8
//if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
//if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) FLAG_SET(flags, SDL_WINDOW_TRANSPARENT); // Alternative: SDL_GL_ALPHA_SIZE = 8
//if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_DESKTOP)) FLAG_SET(flags, SDL_WINDOW_FULLSCREEN_DESKTOP);
// NOTE: Some OpenGL context attributes must be set before window creation
@ -2004,7 +1994,7 @@ int InitPlatform(void)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
}
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
@ -2047,7 +2037,7 @@ int InitPlatform(void)
if (platform.glContext != NULL)
{
SDL_GL_SetSwapInterval((CORE.Window.flags & FLAG_VSYNC_HINT)? 1 : 0);
SDL_GL_SetSwapInterval((FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT))? 1: 0);
// Load OpenGL extensions
// NOTE: GL procedures address loader is required to load extensions
@ -2070,33 +2060,28 @@ int InitPlatform(void)
platform.gamepadId[i] = -1; // Set all gamepad initial instance ids as invalid to not conflict with instance id zero
}
int numJoysticks = 0;
SDL_JoystickID *joysticks = SDL_GetJoysticks(&numJoysticks); // array of joystick IDs, they do not start from 0
int numJoysticks = SDL_NumJoysticks();
if (joysticks)
for (int i = 0; (i < numJoysticks) && (i < MAX_GAMEPADS); i++)
{
for (int i = 0; (i < numJoysticks) && (i < MAX_GAMEPADS); i++)
{
platform.gamepad[i] = SDL_GameControllerOpen(joysticks[i]);
platform.gamepadId[i] = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[i]));
platform.gamepad[i] = SDL_GameControllerOpen(i);
platform.gamepadId[i] = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[i]));
if (platform.gamepad[i])
{
CORE.Input.Gamepad.ready[i] = true;
CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i]));
CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), MAX_GAMEPAD_NAME_LENGTH - 1);
CORE.Input.Gamepad.name[i][MAX_GAMEPAD_NAME_LENGTH - 1] = '\0';
}
else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
if (platform.gamepad[i])
{
CORE.Input.Gamepad.ready[i] = true;
CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i]));
CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), MAX_GAMEPAD_NAME_LENGTH - 1);
CORE.Input.Gamepad.name[i][MAX_GAMEPAD_NAME_LENGTH - 1] = '\0';
}
SDL_free(joysticks);
else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
}
// Disable mouse events being interpreted as touch events
// NOTE: This is wanted because there are SDL_FINGER* events available which provide unique data
// Due to the way PollInputEvents() and rgestures.h are currently implemented, setting this won't break SUPPORT_MOUSE_GESTURES
// Due to the way PollInputEvents() and rgestures.h are currently implemented, setting this won't break SUPPORT_MOUSE_GESTURES
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
@ -2212,7 +2197,7 @@ static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event)
for (int i = 0; i < CORE.Input.Touch.pointCount; i++)
{
SDL_Finger *finger = SDL_GetTouchFinger(event.touchId, i);
CORE.Input.Touch.pointId[i] = finger->id;
CORE.Input.Touch.pointId[i] = (int)finger->id;
CORE.Input.Touch.position[i].x = finger->x*CORE.Window.screen.width;
CORE.Input.Touch.position[i].y = finger->y*CORE.Window.screen.height;
CORE.Input.Touch.currentTouchState[i] = 1;

View file

@ -72,6 +72,8 @@
#include <shellscalingapi.h>
#include <versionhelpers.h>
#include <malloc.h> // Required for alloca()
#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
#include <GL/gl.h>
#endif
@ -195,8 +197,8 @@ static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = NULL;
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
//----------------------------------------------------------------------------------
// Types and Structures Definition
@ -260,9 +262,9 @@ static bool DecoratedFromStyle(DWORD style)
// Get window style from required flags
static DWORD MakeWindowStyle(unsigned flags)
{
// We don't need this since we don't have any child windows, but I guess
// it improves efficiency, plus, windows adds this flag automatically anyway
// so it keeps our flags in sync with the OS
// Flag is not needed because there are no child windows,
// but supposedly it improves efficiency, plus, windows adds this
// flag automatically anyway so it keeps flags in sync with the OS
DWORD style = WS_CLIPSIBLINGS;
style |= (flags & FLAG_WINDOW_HIDDEN)? 0 : WS_VISIBLE;
@ -271,7 +273,7 @@ static DWORD MakeWindowStyle(unsigned flags)
// Minimized takes precedence over maximized
int mized = MIZED_NONE;
if (FLAG_CHECK(flags, FLAG_WINDOW_MINIMIZED)) mized = MIZED_MIN;
if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)) mized = MIZED_MIN;
if (flags & FLAG_WINDOW_MAXIMIZED) mized = MIZED_MAX;
switch (mized)
@ -937,7 +939,7 @@ void SetWindowIcon(Image image)
// Set icon for window
void SetWindowIcons(Image *images, int count)
{
// TODO.
// TODO: Implement SetWindowIcons()
}
void SetWindowTitle(const char *title)
@ -1228,7 +1230,7 @@ void SwapScreenBuffer(void)
// Get elapsed time measure in seconds
double GetTime(void)
{
LARGE_INTEGER now;
LARGE_INTEGER now = 0;
QueryPerformanceCounter(&now);
return (double)(now.QuadPart - CORE.Time.base)/(double)platform.timerFrequency.QuadPart;
}
@ -1237,14 +1239,18 @@ double GetTime(void)
// NOTE: This function is only safe to use if you control the URL given
// A user could craft a malicious string performing another action
// Only call this function yourself not with user input or make sure to check the string yourself
// Ref: https://github.com/raysan5/raylib/issues/686
// REF: https://github.com/raysan5/raylib/issues/686
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code on target platform
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
{
TRACELOG(LOG_WARNING, "OpenURL not implemented");
char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
sprintf(cmd, "explorer \"%s\"", url);
int result = system(cmd);
if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
RL_FREE(cmd);
}
}
@ -1871,13 +1877,23 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lpara
} break;
case WM_DPICHANGED:
{
// Get current dpi scale factor
float scalex = HIWORD(wParam)/96.0f;
float scaley = LOWORD(wParam)/96.0f;
RECT *suggestedRect = (RECT *)lparam;
// Never set the window size to anything other than the suggested rect here
// Doing so can cause a window to stutter between monitors when transitioning between them
int result = (int)SetWindowPos(hwnd, NULL, suggestedRect->left, suggestedRect->top,
suggestedRect->right - suggestedRect->left, suggestedRect->bottom - suggestedRect->top, SWP_NOZORDER | SWP_NOACTIVATE);
int result = (int)SetWindowPos(hwnd, NULL,
suggestedRect->left, suggestedRect->top,
suggestedRect->right - suggestedRect->left,
suggestedRect->bottom - suggestedRect->top,
SWP_NOZORDER | SWP_NOACTIVATE);
if (result == 0) TRACELOG(LOG_ERROR, "Failed to set window position [ERROR: %lu]", GetLastError());
// TODO: Update screen data, render size, screen scaling, viewport...
} break;
case WM_SETCURSOR:
@ -1981,7 +1997,7 @@ static void HandleKey(WPARAM wparam, LPARAM lparam, char state)
{
CORE.Input.Keyboard.currentKeyState[key] = state;
if ((key == KEY_ESCAPE) && (state == 1)) CORE.Window.shouldClose = 1;
if ((key == KEY_ESCAPE) && (state == 1)) CORE.Window.shouldClose = true;
}
else TRACELOG(LOG_WARNING, "INPUT: Unknown (or currently unhandled) virtual keycode %d (0x%x)", wparam, wparam);
@ -2033,8 +2049,7 @@ static void HandleWindowResize(HWND hwnd, int *width, int *height)
// TODO: Update framebuffer on resize
CORE.Window.currentFbo.width = (int)clientSize.cx;
CORE.Window.currentFbo.height = (int)clientSize.cy;
//glViewport(0, 0, clientSize.cx, clientSize.cy);
//SetupFramebuffer(0, 0);
//SetupViewport(0, 0, clientSize.cx, clientSize.cy);
SetupViewport(clientSize.cx, clientSize.cy);
CORE.Window.resizedLastFrame = true;

View file

@ -224,7 +224,7 @@ static const short linuxToRaylibMap[KEYMAP_SIZE] = {
248, 0, 0, 0, 0, 0, 0, 0,
// Gamepads are mapped according to:
// https://www.kernel.org/doc/html/next/input/gamepad.html
// REF: https://www.kernel.org/doc/html/next/input/gamepad.html
// Those mappings are standardized, but that doesn't mean people follow
// the standards, so this is more of an approximation
[BTN_DPAD_UP] = GAMEPAD_BUTTON_LEFT_FACE_UP,
@ -269,6 +269,8 @@ static int FindMatchingConnectorMode(const drmModeConnector *connector, const dr
static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list
static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search the nearest matching DRM connector mode in connector's list
static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform())
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
@ -641,7 +643,7 @@ static uint32_t GetOrCreateFbForBo(struct gbm_bo *bo)
}
// Renders a blank frame to allocate initial buffers
// TODO: WARNING: Platform layers do not include OpenGL code!
// TODO: WARNING: Platform backend should not include OpenGL code
void RenderBlankFrame()
{
glClearColor(0, 0, 0, 1);
@ -828,15 +830,6 @@ void SwapScreenBuffer(void)
return;
}
// Get the software rendered color buffer
int bufferWidth = 0, bufferHeight = 0;
void *colorBuffer = swGetColorBuffer(&bufferWidth, &bufferHeight);
if (!colorBuffer)
{
TRACELOG(LOG_ERROR, "DISPLAY: Failed to get software color buffer");
return;
}
// Retrieving the dimensions of the display mode used
drmModeModeInfo *mode = &platform.connector->modes[platform.modeIndex];
uint32_t width = mode->hdisplay;
@ -904,16 +897,8 @@ void SwapScreenBuffer(void)
}
// Copy the software rendered buffer to the dumb buffer with scaling if needed
if (bufferWidth == width && bufferHeight == height)
{
// Direct copy if sizes match
swCopyFramebuffer(0, 0, bufferWidth, bufferHeight, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer);
}
else
{
// Scale the software buffer to match the display mode
swBlitFramebuffer(0, 0, width, height, 0, 0, bufferWidth, bufferHeight, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer);
}
// NOTE: RLSW will make a simple copy if the dimensions match
swBlitFramebuffer(0, 0, width, height, 0, 0, width, height, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer);
// Unmap the buffer
munmap(dumbBuffer, creq.size);
@ -1034,7 +1019,7 @@ double GetTime(void)
// NOTE: This function is only safe to use if you control the URL given
// A user could craft a malicious string performing another action
// Only call this function yourself not with user input or make sure to check the string yourself
// Ref: https://github.com/raysan5/raylib/issues/686
// REF: https://github.com/raysan5/raylib/issues/686
void OpenURL(const char *url)
{
TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform");
@ -1165,8 +1150,7 @@ int InitPlatform(void)
// Initialize graphic device: display/window and graphic context
//----------------------------------------------------------------------------
CORE.Window.fullscreen = true;
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
#if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
@ -1231,9 +1215,9 @@ int InitPlatform(void)
TRACELOG(LOG_TRACE, "DISPLAY: Connector %i modes detected: %i", i, con->count_modes);
TRACELOG(LOG_TRACE, "DISPLAY: Connector %i status: %s", i,
(con->connection == DRM_MODE_CONNECTED) ? "CONNECTED" :
(con->connection == DRM_MODE_DISCONNECTED) ? "DISCONNECTED" :
(con->connection == DRM_MODE_UNKNOWNCONNECTION) ? "UNKNOWN" : "OTHER");
(con->connection == DRM_MODE_CONNECTED)? "CONNECTED" :
(con->connection == DRM_MODE_DISCONNECTED)? "DISCONNECTED" :
(con->connection == DRM_MODE_UNKNOWNCONNECTION)? "UNKNOWN" : "OTHER");
// In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected
// This might be a hardware or software limitation like on Raspberry Pi Zero with composite output
@ -1315,8 +1299,8 @@ int InitPlatform(void)
CORE.Window.screen.height = CORE.Window.display.height;
}
const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT;
const int fps = (CORE.Time.target > 0) ? (1.0/CORE.Time.target) : 60;
const bool allowInterlaced = FLAG_IS_SET(CORE.Window.flags, FLAG_INTERLACED_HINT);
const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60;
// Try to find an exact matching mode
platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
@ -1346,7 +1330,7 @@ int InitPlatform(void)
TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name,
platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay,
(platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p',
FLAG_IS_SET(platform.connector->modes[platform.modeIndex].flags, DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
platform.connector->modes[platform.modeIndex].vrefresh);
drmModeFreeEncoder(enc);
@ -1363,7 +1347,7 @@ int InitPlatform(void)
platform.connector->modes[0].name,
platform.connector->modes[0].hdisplay,
platform.connector->modes[0].vdisplay,
(platform.connector->modes[0].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p',
(platform.connector->modes[0].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
platform.connector->modes[0].vrefresh);
}
else
@ -1402,7 +1386,7 @@ int InitPlatform(void)
EGLint samples = 0;
EGLint sampleBuffer = 0;
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
samples = 4;
sampleBuffer = 1;
@ -1474,7 +1458,7 @@ int InitPlatform(void)
// find the EGL config that matches the previously setup GBM format
int found = 0;
for (EGLint i = 0; i < matchingNumConfigs; ++i)
for (EGLint i = 0; i < matchingNumConfigs; i++)
{
EGLint id = 0;
if (!eglGetConfigAttrib(platform.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
@ -1579,17 +1563,17 @@ int InitPlatform(void)
TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
#endif
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
// If graphic device is no properly initialized, we end program
if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
// Set some default window flags
CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false
CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false
CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true
CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // false
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // false
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED); // true
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // false
//----------------------------------------------------------------------------
// Initialize timing system
@ -1738,8 +1722,8 @@ static void InitKeyboard(void)
// New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
// NOTE: ISIG controls if ^C and ^Z generate break signals or not
keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG);
//keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
FLAG_CLEAR(keyboardNewSettings.c_lflag, ICANON | ECHO | ISIG);
//FLAG_CLEAR(keyboardNewSettings.c_iflag, ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
keyboardNewSettings.c_cc[VMIN] = 1;
keyboardNewSettings.c_cc[VTIME] = 0;
@ -1758,10 +1742,10 @@ static void InitKeyboard(void)
else
{
// Reconfigure keyboard mode to get:
// - scancodes (K_RAW)
// - keycodes (K_MEDIUMRAW)
// - ASCII chars (K_XLATE)
// - UNICODE chars (K_UNICODE)
// - scancodes (K_RAW)
// - keycodes (K_MEDIUMRAW)
// - ASCII chars (K_XLATE)
// - UNICODE chars (K_UNICODE)
ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); // ASCII chars
}
@ -1883,7 +1867,7 @@ static void ProcessKeyboard(void)
}
#endif // SUPPORT_SSH_KEYBOARD_RPI
// Initialise user input from evdev(/dev/input/event<N>)
// Initialize user input from evdev(/dev/input/event<N>)
// this means mouse, keyboard or gamepad devices
static void InitEvdevInput(void)
{
@ -1891,12 +1875,12 @@ static void InitEvdevInput(void)
DIR *directory = NULL;
struct dirent *entity = NULL;
// Initialise keyboard file descriptor
// Initialize keyboard file descriptor
platform.keyboardFd = -1;
platform.mouseFd = -1;
// Reset variables
for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
for (int i = 0; i < MAX_TOUCH_POINTS; i++)
{
CORE.Input.Touch.position[i].x = -1;
CORE.Input.Touch.position[i].y = -1;
@ -2578,7 +2562,7 @@ static int FindMatchingConnectorMode(const drmModeConnector *connector, const dr
for (size_t i = 0; i < connector->count_modes; i++)
{
TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
connector->modes[i].vrefresh, (FLAG_IS_SET(connector->modes[i].flags, DRM_MODE_FLAG_INTERLACE) > 0)? "interlaced" : "progressive");
if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i;
}
@ -2599,9 +2583,9 @@ static int FindExactConnectorMode(const drmModeConnector *connector, uint width,
{
const drmModeModeInfo *const mode = &platform.connector->modes[i];
TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0)? "interlaced" : "progressive");
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && !allowInterlaced) continue;
if ((FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0) && !allowInterlaced) continue;
if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i;
}
@ -2625,7 +2609,7 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
const drmModeModeInfo *const mode = &platform.connector->modes[i];
TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
(mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
(FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0)? "interlaced" : "progressive");
if ((mode->hdisplay < width) || (mode->vdisplay < height))
{
@ -2633,13 +2617,13 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
continue;
}
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && !allowInterlaced)
if ((FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0) && !allowInterlaced)
{
TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode");
continue;
}
const int unusedPixels = (mode->hdisplay - width) * (mode->vdisplay - height);
const int unusedPixels = (mode->hdisplay - width)*(mode->vdisplay - height);
const int fpsDiff = mode->vrefresh - fps;
if ((unusedPixels < minUnusedPixels) ||
@ -2655,4 +2639,82 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt
return nearestIndex;
}
// Compute framebuffer size relative to screen size and display size
// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
static void SetupFramebuffer(int width, int height)
{
// Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
{
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);
// Downscaling to fit display with border-bars
float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
if (widthRatio <= heightRatio)
{
CORE.Window.render.width = CORE.Window.display.width;
CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
CORE.Window.renderOffset.x = 0;
CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
}
else
{
CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
CORE.Window.render.height = CORE.Window.display.height;
CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
CORE.Window.renderOffset.y = 0;
}
// Screen scaling required
float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
// NOTE: We render to full display resolution!
// We just need to calculate above parameters for downscale matrix and offsets
CORE.Window.render.width = CORE.Window.display.width;
CORE.Window.render.height = CORE.Window.display.height;
TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
}
else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
{
// Required screen size is smaller than display size
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);
if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
{
CORE.Window.screen.width = CORE.Window.display.width;
CORE.Window.screen.height = CORE.Window.display.height;
}
// Upscaling to fit display with border-bars
float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
if (displayRatio <= screenRatio)
{
CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
CORE.Window.renderOffset.x = 0;
CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
}
else
{
CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
CORE.Window.render.height = CORE.Window.screen.height;
CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
CORE.Window.renderOffset.y = 0;
}
}
else
{
CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = CORE.Window.screen.height;
CORE.Window.renderOffset.x = 0;
CORE.Window.renderOffset.y = 0;
}
}
// EOF

View file

@ -0,0 +1,594 @@
/**********************************************************************************************
*
* rcore_memory - Functions to manage window, graphics device and inputs
*
* PLATFORM: MEMORY (No OS)
* - Memory framebuffer output (no os)
*
* LIMITATIONS:
* - Software renderer (rlsw)
* - No input system
*
* POSSIBLE IMPROVEMENTS:
* - Improvement 01
* - Improvement 02
*
* ADDITIONAL NOTES:
* - TRACELOG() function is located in raylib [utils] module
*
* CONFIGURATION:
* #define RCORE_PLATFORM_CUSTOM_FLAG
* Custom flag for rcore on target platform -not used-
*
* DEPENDENCIES:
* - rlsw: Software renderer
* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
*
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2025 Ramon Santamaria (@raysan5) and contributors
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose, including commercial
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim that you
* wrote the original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
* as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
#if defined(_WIN32)
#include <conio.h> // Required for: kbhit()
#else
// Provide kbhit() function in non-Windows platforms
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#endif
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// Platform-specific required data for timming (Win32)
#if defined(_WIN32)
typedef struct _LARGE_INTEGER { int64_t QuadPart; } LARGE_INTEGER;
__declspec(dllimport) int __stdcall QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
__declspec(dllimport) int __stdcall QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
#endif
typedef struct {
unsigned int *pixels; // Pointer to pixel data buffer (RGBA8888 format)
#if defined(_WIN32)
LARGE_INTEGER timerFrequency;
#endif
} PlatformData;
//----------------------------------------------------------------------------------
// Global Variables Definition
//----------------------------------------------------------------------------------
extern CoreData CORE; // Global CORE state context
static PlatformData platform = { 0 }; // Platform specific data
//----------------------------------------------------------------------------------
// Module Internal Functions Declaration
//----------------------------------------------------------------------------------
int InitPlatform(void); // Initialize platform (graphics, inputs and more)
bool InitGraphicsDevice(void); // Initialize graphics device
//----------------------------------------------------------------------------------
// Module Functions Declaration
//----------------------------------------------------------------------------------
// NOTE: Functions declaration is provided by raylib.h
//----------------------------------------------------------------------------------
// Module Internal Functions Declaration
//----------------------------------------------------------------------------------
#if !defined(_WIN32)
static int kbhit(void); // Check if a key has been pressed
static char getch(void) { return getchar(); } // Get pressed character
#endif
//----------------------------------------------------------------------------------
// Module Functions Definition: Window and Graphics Device
//----------------------------------------------------------------------------------
// Check if application should close
bool WindowShouldClose(void)
{
if (CORE.Window.ready) return CORE.Window.shouldClose;
else return true;
}
// Toggle fullscreen mode
void ToggleFullscreen(void)
{
TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
}
// Toggle borderless windowed mode
void ToggleBorderlessWindowed(void)
{
TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
}
// Set window state: maximized, if resizable
void MaximizeWindow(void)
{
TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
}
// Set window state: minimized
void MinimizeWindow(void)
{
TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
}
// Restore window from being minimized/maximized
void RestoreWindow(void)
{
TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
}
// Set window configuration state using flags
void SetWindowState(unsigned int flags)
{
TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
}
// Clear window configuration state flags
void ClearWindowState(unsigned int flags)
{
TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
}
// Set icon for window
void SetWindowIcon(Image image)
{
TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
}
// Set icon for window
void SetWindowIcons(Image *images, int count)
{
TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
}
// Set title for window
void SetWindowTitle(const char *title)
{
CORE.Window.title = title;
}
// Set window position on screen (windowed mode)
void SetWindowPosition(int x, int y)
{
TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
}
// Set monitor for the current window
void SetWindowMonitor(int monitor)
{
TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
}
// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
void SetWindowMinSize(int width, int height)
{
CORE.Window.screenMin.width = width;
CORE.Window.screenMin.height = height;
}
// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
void SetWindowMaxSize(int width, int height)
{
CORE.Window.screenMax.width = width;
CORE.Window.screenMax.height = height;
}
// Set window dimensions
void SetWindowSize(int width, int height)
{
TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
}
// Set window opacity, value opacity is between 0.0 and 1.0
void SetWindowOpacity(float opacity)
{
TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
}
// Set window focused
void SetWindowFocused(void)
{
TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
}
// Get native window handle
void *GetWindowHandle(void)
{
TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
return NULL;
}
// Get number of monitors
int GetMonitorCount(void)
{
TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
return 1;
}
// Get current monitor where window is placed
int GetCurrentMonitor(void)
{
TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
return 0;
}
// Get selected monitor position
Vector2 GetMonitorPosition(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
return (Vector2){ 0, 0 };
}
// Get selected monitor width (currently used by monitor)
int GetMonitorWidth(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform");
return 0;
}
// Get selected monitor height (currently used by monitor)
int GetMonitorHeight(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform");
return 0;
}
// Get selected monitor physical width in millimetres
int GetMonitorPhysicalWidth(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
return 0;
}
// Get selected monitor physical height in millimetres
int GetMonitorPhysicalHeight(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
return 0;
}
// Get selected monitor refresh rate
int GetMonitorRefreshRate(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
return 0;
}
// Get the human-readable, UTF-8 encoded name of the selected monitor
const char *GetMonitorName(int monitor)
{
TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform");
return "";
}
// Get window position XY on monitor
Vector2 GetWindowPosition(void)
{
TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform");
return (Vector2){ 0, 0 };
}
// Get window scale DPI factor for current monitor
Vector2 GetWindowScaleDPI(void)
{
TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
return (Vector2){ 1.0f, 1.0f };
}
// Set clipboard text content
void SetClipboardText(const char *text)
{
TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
}
// Get clipboard text content
// NOTE: returned string is allocated and freed by GLFW
const char *GetClipboardText(void)
{
TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
return NULL;
}
// Get clipboard image
Image GetClipboardImage(void)
{
Image image = { 0 };
TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
return image;
}
// Show mouse cursor
void ShowCursor(void)
{
CORE.Input.Mouse.cursorHidden = false;
}
// Hides mouse cursor
void HideCursor(void)
{
CORE.Input.Mouse.cursorHidden = true;
}
// Enables cursor (unlock cursor)
void EnableCursor(void)
{
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
CORE.Input.Mouse.cursorHidden = false;
}
// Disables cursor (lock cursor)
void DisableCursor(void)
{
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
CORE.Input.Mouse.cursorHidden = true;
}
// Swap back buffer with front buffer (screen drawing)
void SwapScreenBuffer(void)
{
// Update framebuffer
rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels);
}
//----------------------------------------------------------------------------------
// Module Functions Definition: Misc
//----------------------------------------------------------------------------------
// Get elapsed time measure in seconds since InitTimer()
double GetTime(void)
{
double time = 0.0;
#if defined(_WIN32)
LARGE_INTEGER now = { 0 };
QueryPerformanceCounter(&now);
return (double)(now.QuadPart - CORE.Time.base)/(double)platform.timerFrequency.QuadPart;
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
struct timespec ts = { 0 };
clock_gettime(CLOCK_MONOTONIC, &ts);
unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
#endif
return time;
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if you control the URL given.
// A user could craft a malicious string performing another action.
// Only call this function yourself not with user input or make sure to check the string yourself.
// REF: https://github.com/raysan5/raylib/issues/686
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code on target platform
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
{
char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
sprintf(cmd, "explorer \"%s\"", url);
int result = system(cmd);
if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
RL_FREE(cmd);
}
}
//----------------------------------------------------------------------------------
// Module Functions Definition: Inputs
//----------------------------------------------------------------------------------
// Set internal gamepad mappings
int SetGamepadMappings(const char *mappings)
{
TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
return 0;
}
// Set gamepad vibration
void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
{
TRACELOG(LOG_WARNING, "SetGamepadVibration() not implemented on target platform");
}
// Set mouse position XY
void SetMousePosition(int x, int y)
{
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
}
// Set mouse cursor
void SetMouseCursor(int cursor)
{
TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
}
// Get physical key name
const char *GetKeyName(int key)
{
TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
return "";
}
// Register all input events
void PollInputEvents(void)
{
#if defined(SUPPORT_GESTURES_SYSTEM)
// NOTE: Gestures update must be called every frame to reset gestures correctly
// because ProcessGestureEvent() is just called on an event, not every frame
UpdateGestures();
#endif
// Reset keys/chars pressed registered
CORE.Input.Keyboard.keyPressedQueueCount = 0;
CORE.Input.Keyboard.charPressedQueueCount = 0;
// Reset key repeats
for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
// Reset last gamepad button/axis registered state
CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
//CORE.Input.Gamepad.axisCount = 0;
// Register previous touch states
for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
// Reset touch positions
// TODO: It resets on target platform the mouse position and not filled again until a move-event,
// so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
//for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
// Register previous keys states
// NOTE: Android supports up to 260 keys
for (int i = 0; i < 260; i++)
{
CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
}
// TODO: Poll input events for current platform
// Check for key pressed to exit
if (kbhit())
{
int key = getch();
if (key == 27) CORE.Window.shouldClose = true; // KEY_SCAPE
}
}
//----------------------------------------------------------------------------------
// Module Internal Functions Definition
//----------------------------------------------------------------------------------
// Initialize platform: graphics, inputs and more
int InitPlatform(void)
{
// Memory framebuffer can only work with software renderer
if (rlGetVersion() != RL_OPENGL_11_SOFTWARE)
{
TRACELOG(LOG_WARNING, "DISPLAY: Memory platform requires software renderer (GRAPHICS_API_OPENGL_11_SOFTWARE)");
TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
return -1;
}
else
{
// Load memory framebuffer with desired screen size
platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(int));
}
//----------------------------------------------------------------------------
// If everything work as expected, we can continue
CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = CORE.Window.screen.height;
CORE.Window.currentFbo.width = CORE.Window.render.width;
CORE.Window.currentFbo.height = CORE.Window.render.height;
TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height);
TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
CORE.Window.ready = true;
// TODO: Load OpenGL extensions
// NOTE: GL procedures address loader is required to load extensions
//----------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------
// TODO: Initialize input events system
// It could imply keyboard, mouse, gamepad, touch...
// Depending on the platform libraries/SDK it could use a callback mechanism
// For system events and inputs evens polling on a per-frame basis, use PollInputEvents()
//----------------------------------------------------------------------------
// ...
//----------------------------------------------------------------------------
// Initialize timing system
//----------------------------------------------------------------------------
#if defined(_WIN32)
LARGE_INTEGER time = { 0 };
QueryPerformanceCounter(&time);
QueryPerformanceFrequency(&platform.timerFrequency);
CORE.Time.base = time.QuadPart;
#endif
InitTimer();
//----------------------------------------------------------------------------
// Initialize storage system
//----------------------------------------------------------------------------
CORE.Storage.basePath = GetWorkingDirectory();
//----------------------------------------------------------------------------
TRACELOG(LOG_INFO, "PLATFORM: MEMORY: Initialized successfully");
return 0;
}
// Close platform
void ClosePlatform(void)
{
RL_FREE(platform.pixels);
}
//----------------------------------------------------------------------------------
// Module Internal Functions Definition
//----------------------------------------------------------------------------------
#if !defined(_WIN32)
// Check if a key has been pressed
static int kbhit(void)
{
struct termios oldt = { 0 };
struct termios newt = { 0 };
int ch = 0;
int oldf = 0;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if (ch != EOF)
{
ungetc(ch, stdin);
return 1;
}
return 0;
}
#endif
// EOF

View file

@ -54,11 +54,6 @@
typedef struct {
// TODO: Define the platform specific variables required
// Display data
EGLDisplay device; // Native display device (physical screen connection)
EGLSurface surface; // Surface to draw on, framebuffers (connected to context)
EGLContext context; // Graphic context, mode in which drawing can be done
EGLConfig config; // Graphic config
} PlatformData;
//----------------------------------------------------------------------------------
@ -346,10 +341,10 @@ void SwapScreenBuffer(void)
double GetTime(void)
{
double time = 0.0;
struct timespec ts = { 0 };
clock_gettime(CLOCK_MONOTONIC, &ts);
unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer()
return time;
@ -366,7 +361,7 @@ void OpenURL(const char *url)
if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
else
{
// TODO:
// TODO: Load url using default browser
}
}
@ -459,89 +454,20 @@ int InitPlatform(void)
// raylib uses OpenGL so, platform should create that kind of connection
// Below example illustrates that process using EGL library
//----------------------------------------------------------------------------
CORE.Window.fullscreen = true;
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
EGLint samples = 0;
EGLint sampleBuffer = 0;
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
samples = 4;
sampleBuffer = 1;
// TODO: Enable MSAA
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
}
const EGLint framebufferAttribs[] =
{
EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support
EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5)
EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6)
EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5)
//EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!)
//EGL_STENCIL_SIZE, 8, // Stencil buffer size
EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs)
EGL_NONE
};
// TODO: Init display and graphic device
const EGLint contextAttribs[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLint numConfigs = 0;
// Get an EGL device connection
platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (platform.device == EGL_NO_DISPLAY)
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
return false;
}
// Initialize the EGL device connection
if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
{
// If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
return false;
}
// Get an appropriate EGL framebuffer configuration
eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs);
// Set rendering API
eglBindAPI(EGL_OPENGL_ES_API);
// Create an EGL rendering context
platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
if (platform.context == EGL_NO_CONTEXT)
{
TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
return -1;
}
// Create an EGL window surface
EGLint displayFormat = 0;
// EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
// As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
// Android specific call
ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size
platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL);
// There must be at least one frame displayed before the buffers are swapped
eglSwapInterval(platform.device, 1);
EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
// Check surface and context activation
if (result != EGL_FALSE)
// TODO: Check display, device and context activation
bool result = true;
if (result)
{
CORE.Window.ready = true;

View file

@ -76,6 +76,13 @@ typedef struct {
bool ourFullscreen; // Internal var to filter our handling of fullscreen vs the user handling of fullscreen
int unmaximizedWidth; // Internal var to store the unmaximized window (canvas) width
int unmaximizedHeight; // Internal var to store the unmaximized window (canvas) height
char canvasId[64]; // Keep current canvas id where wasm app is running
// NOTE: Useful when trying to run multiple wasms in different canvases in same webpage
#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
unsigned int *pixels; // Pointer to pixel data buffer (RGBA 32bit format)
#endif
} PlatformData;
//----------------------------------------------------------------------------------
@ -129,7 +136,7 @@ static void MouseEnterCallback(GLFWwindow *window, int enter);
// Emscripten window callback events
static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
//static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData);
@ -142,7 +149,11 @@ static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPoin
static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
static const char *GetCanvasId(void);
// JS: Set the canvas id provided by the module configuration
EM_JS(void, SetCanvasIdJs, (char *out, int outSize), {
var canvasId = "#" + Module.canvas.id;
stringToUTF8(canvasId, out, outSize);
});
//----------------------------------------------------------------------------------
// Module Functions Declaration
@ -181,8 +192,8 @@ void ToggleFullscreen(void)
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (wasFullscreen)
{
if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterFullscreen = false;
else if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterFullscreen = true;
if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) enterFullscreen = false;
else if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) enterFullscreen = true;
else
{
const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
@ -193,24 +204,21 @@ void ToggleFullscreen(void)
EM_ASM(document.exitFullscreen(););
CORE.Window.fullscreen = false;
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
else enterFullscreen = true;
if (enterFullscreen)
{
// NOTE: The setTimeouts handle the browser mode change delay
EM_ASM
(
setTimeout(function()
{
EM_ASM(
setTimeout(function(){
Module.requestFullscreen(false, false);
}, 100);
);
CORE.Window.fullscreen = true;
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
// NOTE: Old notes below:
@ -227,17 +235,17 @@ void ToggleFullscreen(void)
*/
// EM_ASM(Module.requestFullscreen(false, false););
/*
if (!CORE.Window.fullscreen)
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
// Option 1: Request fullscreen for the canvas element
// This option does not seem to work at all:
// emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security,
// the user must click once on the canvas to hide the pointer or transition to full screen
//emscripten_request_fullscreen("#canvas", false);
//emscripten_request_fullscreen(platform.canvasId, false);
// Option 2: Request fullscreen for the canvas element with strategy
// This option does not seem to work at all
// Ref: https://github.com/emscripten-core/emscripten/issues/5124
// REF: https://github.com/emscripten-core/emscripten/issues/5124
// EmscriptenFullscreenStrategy strategy = {
// .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
// .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
@ -245,7 +253,7 @@ void ToggleFullscreen(void)
// .canvasResizedCallback = EmscriptenWindowResizedCallback,
// .canvasResizedCallbackUserData = NULL
// };
//emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy);
//emscripten_request_fullscreen_strategy(platform.canvasId, EM_FALSE, &strategy);
// Option 3: Request fullscreen for the canvas element with strategy
// It works as expected but only inside the browser (client area)
@ -256,14 +264,14 @@ void ToggleFullscreen(void)
.canvasResizedCallback = EmscriptenWindowResizedCallback,
.canvasResizedCallbackUserData = NULL
};
emscripten_enter_soft_fullscreen("#canvas", &strategy);
emscripten_enter_soft_fullscreen(platform.canvasId, &strategy);
int width, height;
emscripten_get_canvas_element_size("#canvas", &width, &height);
int width = 0;
int height = 0;
emscripten_get_canvas_element_size(platform.canvasId, &width, &height);
TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height);
CORE.Window.fullscreen = true; // Toggle fullscreen flag
CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
else
{
@ -271,11 +279,10 @@ void ToggleFullscreen(void)
//emscripten_exit_soft_fullscreen();
int width, height;
emscripten_get_canvas_element_size("#canvas", &width, &height);
emscripten_get_canvas_element_size(platform.canvasId, &width, &height);
TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height);
CORE.Window.fullscreen = false; // Toggle fullscreen flag
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
*/
}
@ -289,8 +296,8 @@ void ToggleBorderlessWindowed(void)
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (wasFullscreen)
{
if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterBorderless = false;
else if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterBorderless = true;
if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) enterBorderless = false;
else if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) enterBorderless = true;
else
{
const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
@ -301,16 +308,15 @@ void ToggleBorderlessWindowed(void)
EM_ASM(document.exitFullscreen(););
CORE.Window.fullscreen = false;
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
else enterBorderless = true;
if (enterBorderless)
{
// NOTE: 1. The setTimeouts handle the browser mode change delay
// 2. The style unset handles the possibility of a width="value%" like on the default shell.html file
// 1. The setTimeouts handle the browser mode change delay
// 2. The style unset handles the possibility of a width="value%" like on the default shell.html file
EM_ASM
(
setTimeout(function()
@ -322,14 +328,14 @@ void ToggleBorderlessWindowed(void)
}, 100);
}, 100);
);
CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
}
// Set window state: maximized, if resizable
void MaximizeWindow(void)
{
if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && !(CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && !(FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)))
{
platform.unmaximizedWidth = CORE.Window.screen.width;
platform.unmaximizedHeight = CORE.Window.screen.height;
@ -339,7 +345,7 @@ void MaximizeWindow(void)
if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
@ -352,11 +358,11 @@ void MinimizeWindow(void)
// Restore window from being minimized/maximized
void RestoreWindow(void)
{
if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)))
{
if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
@ -369,13 +375,13 @@ void SetWindowState(unsigned int flags)
// NOTE: In most cases the functions already change the flags internally
// State change: FLAG_VSYNC_HINT
if ((flags & FLAG_VSYNC_HINT) > 0)
if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_VSYNC_HINT) not available on target platform");
}
// State change: FLAG_BORDERLESS_WINDOWED_MODE
if ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
// NOTE: Window state flag updated inside ToggleBorderlessWindowed() function
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
@ -383,13 +389,13 @@ void SetWindowState(unsigned int flags)
{
const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0);
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed();
if ((FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed();
}
else ToggleBorderlessWindowed();
}
// State change: FLAG_FULLSCREEN_MODE
if ((flags & FLAG_FULLSCREEN_MODE) > 0)
if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
// NOTE: Window state flag updated inside ToggleFullscreen() function
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
@ -397,38 +403,38 @@ void SetWindowState(unsigned int flags)
{
const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || screenWidth == canvasWidth ) ToggleFullscreen();
if ((FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) || screenWidth == canvasWidth ) ToggleFullscreen();
}
else ToggleFullscreen();
}
// State change: FLAG_WINDOW_RESIZABLE
if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) && (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)))
{
glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
}
// State change: FLAG_WINDOW_UNDECORATED
if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
}
// State change: FLAG_WINDOW_HIDDEN
if ((flags & FLAG_WINDOW_HIDDEN) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
}
// State change: FLAG_WINDOW_MINIMIZED
if ((flags & FLAG_WINDOW_MINIMIZED) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
}
// State change: FLAG_WINDOW_MAXIMIZED
if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED) != FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)))
{
if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
{
@ -440,24 +446,24 @@ void SetWindowState(unsigned int flags)
if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
// State change: FLAG_WINDOW_UNFOCUSED
if ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
}
// State change: FLAG_WINDOW_TOPMOST
if ((flags & FLAG_WINDOW_TOPMOST) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
}
// State change: FLAG_WINDOW_ALWAYS_RUN
if ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
}
@ -466,31 +472,31 @@ void SetWindowState(unsigned int flags)
// NOTE: Review for PLATFORM_WEB
// State change: FLAG_WINDOW_TRANSPARENT
if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
}
// State change: FLAG_WINDOW_HIGHDPI
if ((flags & FLAG_WINDOW_HIGHDPI) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
}
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
if ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
}
// State change: FLAG_MSAA_4X_HINT
if ((flags & FLAG_MSAA_4X_HINT) > 0)
if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
}
// State change: FLAG_INTERLACED_HINT
if ((flags & FLAG_INTERLACED_HINT) > 0)
if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "SetWindowState(FLAG_INTERLACED_HINT) not available on target platform");
}
@ -503,90 +509,89 @@ void ClearWindowState(unsigned int flags)
// NOTE: In most cases the functions already change the flags internally
// State change: FLAG_VSYNC_HINT
if ((flags & FLAG_VSYNC_HINT) > 0)
if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_VSYNC_HINT) not available on target platform");
}
// State change: FLAG_BORDERLESS_WINDOWED_MODE
if ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
{
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (wasFullscreen)
{
const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen(););
if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen(););
}
CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
// State change: FLAG_FULLSCREEN_MODE
if ((flags & FLAG_FULLSCREEN_MODE) > 0)
if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
{
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (wasFullscreen)
{
const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0);
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen(););
if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen(););
}
CORE.Window.fullscreen = false;
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
}
// State change: FLAG_WINDOW_RESIZABLE
if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) && (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)))
{
glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
}
// State change: FLAG_WINDOW_HIDDEN
if ((flags & FLAG_WINDOW_HIDDEN) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
}
// State change: FLAG_WINDOW_MINIMIZED
if ((flags & FLAG_WINDOW_MINIMIZED) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
}
// State change: FLAG_WINDOW_MAXIMIZED
if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)))
{
if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
{
if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
}
}
// State change: FLAG_WINDOW_UNDECORATED
if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
}
// State change: FLAG_WINDOW_UNFOCUSED
if ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
}
// State change: FLAG_WINDOW_TOPMOST
if ((flags & FLAG_WINDOW_TOPMOST) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
}
// State change: FLAG_WINDOW_ALWAYS_RUN
if ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
}
@ -595,31 +600,31 @@ void ClearWindowState(unsigned int flags)
// NOTE: Review for PLATFORM_WEB
// State change: FLAG_WINDOW_TRANSPARENT
if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
}
// State change: FLAG_WINDOW_HIGHDPI
if ((flags & FLAG_WINDOW_HIGHDPI) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
}
// State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
if ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
}
// State change: FLAG_MSAA_4X_HINT
if ((flags & FLAG_MSAA_4X_HINT) > 0)
if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
}
// State change: FLAG_INTERLACED_HINT
if ((flags & FLAG_INTERLACED_HINT) > 0)
if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
{
TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_INTERLACED_HINT) not available on target platform");
}
@ -663,7 +668,7 @@ void SetWindowMinSize(int width, int height)
CORE.Window.screenMin.height = height;
// Trigger the resize event once to update the window minimum width and height
if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
}
// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
@ -673,7 +678,7 @@ void SetWindowMaxSize(int width, int height)
CORE.Window.screenMax.height = height;
// Trigger the resize event once to update the window maximum width and height
if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
}
// Set window dimensions
@ -866,8 +871,7 @@ void EnableCursor(void)
// Disables cursor (lock cursor)
void DisableCursor(void)
{
// TODO: figure out how not to hard code the canvas ID here.
emscripten_request_pointerlock(GetCanvasId(), 1);
emscripten_request_pointerlock(platform.canvasId, 1);
// Set cursor position in the middle
SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
@ -878,7 +882,32 @@ void DisableCursor(void)
// Swap back buffer with front buffer (screen drawing)
void SwapScreenBuffer(void)
{
#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
// Update framebuffer
rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels);
// Copy framebuffer data into canvas
EM_ASM({
const width = $0;
const height = $1;
const ptr = $2;
// Get canvas and 2d context created
const canvas = Module.canvas;
const ctx = canvas.getContext('2d');
if (!Module.__img || (Module.__img.width !== width) || (Module.__img.height !== height)) {
Module.__img = ctx.createImageData(width, height);
}
const src = HEAPU8.subarray(ptr, ptr + width*height*4); // RGBA (4 bytes)
Module.__img.data.set(src);
ctx.putImageData(Module.__img, 0, 0);
}, CORE.Window.screen.width, CORE.Window.screen.height, platform.pixels);
#else
glfwSwapBuffers(platform.handle);
#endif
}
//----------------------------------------------------------------------------------
@ -893,10 +922,9 @@ double GetTime(void)
}
// Open URL with default system browser (if available)
// NOTE: This function is only safe to use if you control the URL given.
// A user could craft a malicious string performing another action.
// Only call this function yourself not with user input or make sure to check the string yourself.
// Ref: https://github.com/raysan5/raylib/issues/686
// NOTE: This function is only safe to use if you control the URL given
// A user could craft a malicious string performing another action
// Only call this function yourself not with user input or make sure to check the string yourself
void OpenURL(const char *url)
{
// Security check to (partially) avoid malicious code on target platform
@ -970,7 +998,7 @@ void SetMouseCursor(int cursor)
}
}
// Get physical key name.
// Get physical key name
const char *GetKeyName(int key)
{
TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
@ -1090,10 +1118,6 @@ void PollInputEvents(void)
}
CORE.Window.resizedLastFrame = false;
// TODO: This code does not seem to do anything??
//if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused)
//else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) --> WARNING: Where is key input reset?
}
//----------------------------------------------------------------------------------
@ -1103,6 +1127,8 @@ void PollInputEvents(void)
// Initialize platform: graphics, inputs and more
int InitPlatform(void)
{
SetCanvasIdJs(platform.canvasId, 64); // Get the current canvas id
glfwSetErrorCallback(ErrorCallback);
// Initialize GLFW internal global state
@ -1122,27 +1148,25 @@ int InitPlatform(void)
// glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers
// Check window creation flags
if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden
if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window
if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable
// Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
// Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
// NOTE: Some GLFW flags are not supported on HTML5
@ -1150,10 +1174,10 @@ int InitPlatform(void)
// Scale content area based on the monitor content scale where window is placed on
// NOTE: This feature requires emscripten 3.1.51
//if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
//if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
//else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
{
// NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
@ -1161,8 +1185,8 @@ int InitPlatform(void)
}
// NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version
// with backward compatibility to older OpenGL versions.
// For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context.
// with backward compatibility to older OpenGL versions
// For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context
// Check selection OpenGL version
if (rlGetVersion() == RL_OPENGL_21)
@ -1172,10 +1196,12 @@ int InitPlatform(void)
}
else if (rlGetVersion() == RL_OPENGL_33)
{
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
// Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
// Profiles Hint, only OpenGL 3.3 and above
// Possible values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above!
// glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
}
@ -1198,7 +1224,6 @@ int InitPlatform(void)
}
else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context
{
// TODO: It seems WebGL 2.0 context is not set despite being requested
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
@ -1211,14 +1236,28 @@ int InitPlatform(void)
// Init fullscreen toggle required var:
platform.ourFullscreen = false;
if (CORE.Window.fullscreen)
#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
// Avoid creating a WebGL canvas, avoid calling glfwCreateWindow()
emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height);
EM_ASM({
const canvas = document.getElementById("canvas");
Module.canvas = canvas;
});
// Load memory framebuffer with desired screen size
// NOTE: Despite using a software framebuffer for blitting, GLFW still creates a WebGL canvas,
// but it is not being used, on SwapScreenBuffer() the pure software renderer is used
// TODO: Consider requesting another type of canvas, not a WebGL one --> Replace GLFW-web by Emscripten?
platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(unsigned int));
#else
if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
{
// remember center for switchinging from fullscreen to window
if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
{
// If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
// Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
// If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed
// Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11
CORE.Window.position.x = CORE.Window.display.width/4;
CORE.Window.position.y = CORE.Window.display.height/4;
}
@ -1251,18 +1290,6 @@ int InitPlatform(void)
TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
// NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
// for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
// framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
// by the sides to fit all monitor space...
// Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
// It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
// Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
// TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
// HighDPI monitors are properly considered in a following similar function: SetupViewport()
SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
// NOTE: Full-screen change, not working properly...
@ -1286,6 +1313,7 @@ int InitPlatform(void)
TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
return -1;
}
#endif
// WARNING: glfwCreateWindow() title doesn't work with emscripten
emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " ");
@ -1296,7 +1324,7 @@ int InitPlatform(void)
glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
glfwSetDropCallback(platform.handle, WindowDropCallback);
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
// Window content (framebuffer) scale callback
glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
@ -1338,7 +1366,7 @@ int InitPlatform(void)
return -1;
}
if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
// If graphic device is no properly initialized, we end program
if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
@ -1352,14 +1380,14 @@ int InitPlatform(void)
//----------------------------------------------------------------------------
// Setup window events callbacks
emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenFullscreenChangeCallback);
emscripten_set_blur_callback(GetCanvasId(), platform.handle, 1, EmscriptenFocusCallback);
emscripten_set_focus_callback(GetCanvasId(), platform.handle, 1, EmscriptenFocusCallback);
emscripten_set_blur_callback(platform.canvasId, platform.handle, 1, EmscriptenFocusCallback);
emscripten_set_focus_callback(platform.canvasId, platform.handle, 1, EmscriptenFocusCallback);
emscripten_set_visibilitychange_callback(NULL, 1, EmscriptenVisibilityChangeCallback);
// WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
// Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
// Check fullscreen change events(note this is done on the window since most browsers don't support this on canvas)
// emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
// Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
// Check Resize event (note this is done on the window since most browsers don't support this on canvas)
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
// Trigger resize callback to force initial size
@ -1367,15 +1395,15 @@ int InitPlatform(void)
// Setup input events
// NOTE: Keyboard callbacks only used to consume some events, libglfw.js takes care of the actual input
//emscripten_set_keypress_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback); // WRNING: Breaks input
//emscripten_set_keydown_callback(GetCanvasId(), NULL, 1, EmscriptenKeyboardCallback);
emscripten_set_click_callback(GetCanvasId(), NULL, 1, EmscriptenMouseCallback);
//emscripten_set_keypress_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback); // WRNING: Breaks input
//emscripten_set_keydown_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback);
emscripten_set_click_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback);
emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback);
emscripten_set_mousemove_callback(GetCanvasId(), NULL, 1, EmscriptenMouseMoveCallback);
emscripten_set_touchstart_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback);
emscripten_set_touchend_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback);
emscripten_set_touchmove_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback);
emscripten_set_touchcancel_callback(GetCanvasId(), NULL, 1, EmscriptenTouchCallback);
emscripten_set_mousemove_callback(platform.canvasId, NULL, 1, EmscriptenMouseMoveCallback);
emscripten_set_touchstart_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
emscripten_set_touchend_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
emscripten_set_touchmove_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
emscripten_set_touchcancel_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
//----------------------------------------------------------------------------
@ -1423,7 +1451,7 @@ static void WindowSizeCallback(GLFWwindow *window, int width, int height)
if (IsWindowFullscreen()) return;
// Set current screen size
if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
{
Vector2 windowScaleDPI = GetWindowScaleDPI();
@ -1448,15 +1476,15 @@ static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float s
// GLFW3: Called on windows minimized/restored
static void WindowIconifyCallback(GLFWwindow *window, int iconified)
{
if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified
else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored
if (iconified) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was iconified
else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED); // The window was restored
}
// GLFW3: Called on windows get/lose focus
static void WindowFocusCallback(GLFWwindow *window, int focused)
{
if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused
else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus
if (focused) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window was focused
else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); // The window lost focus
}
// GLFW3: Called on file-drop over the window
@ -1482,7 +1510,7 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths
for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
{
CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
strcpy(CORE.Window.dropFilepaths[i], paths[i]);
strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1);
}
}
}
@ -1517,8 +1545,8 @@ static void CharCallback(GLFWwindow *window, unsigned int key)
// NOTE: Registers any key down considering OS keyboard layout but
// does not detect action events, those should be managed by user...
// Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
// Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
// REF: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
// REF: https://www.glfw.org/docs/latest/input_guide.html#input_char
// Check if there is space available in the queue
if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
@ -1671,8 +1699,8 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE
eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
for (int i = 0; i < gamepadEvent->numAxes; i++) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
*/
if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS))
@ -1696,7 +1724,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent
// NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but
// we are looking for actual CSS size: canvas.style.width and canvas.style.height
// EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight);
emscripten_get_element_css_size(GetCanvasId(), &canvasWidth, &canvasHeight);
emscripten_get_element_css_size(platform.canvasId, &canvasWidth, &canvasHeight);
for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
{
@ -1714,7 +1742,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent
else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
}
// Update mouse position if we detect a single touch.
// Update mouse position if we detect a single touch
if (CORE.Input.Touch.pointCount == 1)
{
CORE.Input.Mouse.currentPosition.x = CORE.Input.Touch.position[0].x;
@ -1781,9 +1809,8 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte
const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
if (!wasFullscreen)
{
CORE.Window.fullscreen = false;
CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
}
}
@ -1794,7 +1821,7 @@ static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const Emscripte
static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
{
// Don't resize non-resizeable windows
if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1;
if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) return 1;
// This event is called whenever the window changes sizes,
// so the size of the canvas object is explicitly retrieved below
@ -1807,7 +1834,7 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *
if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height;
else if ((height > (int)CORE.Window.screenMax.height) && (CORE.Window.screenMax.height > 0)) height = CORE.Window.screenMax.height;
emscripten_set_canvas_element_size(GetCanvasId(), width, height);
emscripten_set_canvas_element_size(platform.canvasId, width, height);
SetupViewport(width, height); // Reset viewport and projection matrix for new size
@ -1844,27 +1871,10 @@ static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent
// Emscripten: Called on visibility change events
static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData)
{
if (visibilityChangeEvent->hidden) CORE.Window.flags |= FLAG_WINDOW_HIDDEN; // The window was hidden
else CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // The window was restored
if (visibilityChangeEvent->hidden) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // The window was hidden
else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // The window was restored
return 1; // The event was consumed by the callback handler
}
//-------------------------------------------------------------------------------------------------------
// JS: Get the canvas id provided by the module configuration
EM_JS(char *, GetCanvasIdJs, (), {
var canvasId = "#" + Module.canvas.id;
var lengthBytes = lengthBytesUTF8(canvasId) + 1;
var stringOnWasmHeap = _malloc(lengthBytes);
stringToUTF8(canvasId, stringOnWasmHeap, lengthBytes);
return stringOnWasmHeap;
});
// Get canvas id (using embedded JS function)
static const char *GetCanvasId(void)
{
static char *canvasId = NULL;
if (canvasId == NULL) canvasId = GetCanvasIdJs();
return canvasId;
}
// EOF

File diff suppressed because it is too large Load diff

View file

@ -593,7 +593,7 @@ AudioBuffer *LoadAudioBuffer(ma_format format, ma_uint32 channels, ma_uint32 sam
// Init audio buffer values
audioBuffer->volume = 1.0f;
audioBuffer->pitch = 1.0f;
audioBuffer->pan = 0.5f;
audioBuffer->pan = 0.0f; // Center
audioBuffer->callback = NULL;
audioBuffer->processor = NULL;
@ -720,7 +720,7 @@ void SetAudioBufferPitch(AudioBuffer *buffer, float pitch)
// Set pan for an audio buffer
void SetAudioBufferPan(AudioBuffer *buffer, float pan)
{
if (pan < 0.0f) pan = 0.0f;
if (pan < -1.0f) pan = -1.0f;
else if (pan > 1.0f) pan = 1.0f;
if (buffer != NULL)
@ -985,10 +985,10 @@ Sound LoadSoundAlias(Sound source)
audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames;
audioBuffer->data = source.stream.buffer->data;
// initalize the buffer as if it was new
// Initalize the buffer as if it was new
audioBuffer->volume = 1.0f;
audioBuffer->pitch = 1.0f;
audioBuffer->pan = 0.5f;
audioBuffer->pan = 0.0f; // Center
sound.frameCount = source.frameCount;
sound.stream.sampleRate = AUDIO.System.device.sampleRate;
@ -1040,8 +1040,8 @@ void UnloadSoundAlias(Sound alias)
}
// Update sound buffer with new data
// NOTE 1: data format must match sound.stream.sampleSize
// NOTE 2: frameCount must not exceed sound.frameCount
// PARAMS: [data], format must match sound.stream.sampleSize, default 32 bit float - stereo
// PARAMS: [frameCount] must not exceed sound.frameCount
void UpdateSound(Sound sound, const void *data, int frameCount)
{
if (sound.stream.buffer != NULL)
@ -1140,7 +1140,7 @@ bool ExportWaveAsCode(Wave wave, const char *fileName)
// Get file name from path and convert variable name to uppercase
char varFileName[256] = { 0 };
strcpy(varFileName, GetFileNameWithoutExt(fileName));
strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
// Add wave information
@ -2092,9 +2092,7 @@ float GetMusicTimePlayed(Music music)
int framesInFirstBuffer = music.stream.buffer->isSubBufferProcessed[0]? 0 : subBufferSize;
int framesInSecondBuffer = music.stream.buffer->isSubBufferProcessed[1]? 0 : subBufferSize;
int framesInBuffers = framesInFirstBuffer + framesInSecondBuffer;
if ((unsigned int)framesInBuffers > music.frameCount) {
if (!music.looping) framesInBuffers = music.frameCount;
}
if (((unsigned int)framesInBuffers > music.frameCount) && !music.looping) framesInBuffers = music.frameCount;
int framesSentToMix = music.stream.buffer->frameCursorPos%subBufferSize;
int framesPlayed = (framesProcessed - framesInBuffers + framesSentToMix)%(int)music.frameCount;
if (framesPlayed < 0) framesPlayed += music.frameCount;
@ -2125,7 +2123,7 @@ AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, un
if (deviceBitsPerSample > 4) deviceBitsPerSample = 4;
deviceBitsPerSample *= AUDIO.System.device.playback.channels;
unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0) ? (AUDIO.System.device.sampleRate/30*deviceBitsPerSample) : AUDIO.Buffer.defaultSize;
unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0)? (AUDIO.System.device.sampleRate/30*deviceBitsPerSample) : AUDIO.Buffer.defaultSize;
if (subBufferSize < periodSize) subBufferSize = periodSize;
@ -2605,8 +2603,8 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr
if (channels == 2) // We consider panning
{
const float left = buffer->pan;
const float right = 1.0f - left;
const float right = (buffer->pan + 1.0f)/2.0f; // Normalize: [-1..1] -> [0..1]
const float left = 1.0f - right;
// Fast sine approximation in [0..1] for pan law: y = 0.5f*x*(3 - x*x);
const float levels[2] = { localVolume*0.5f*left*(3.0f - left*left), localVolume*0.5f*right*(3.0f - right*right) };
@ -2741,11 +2739,13 @@ static const char *GetFileExtension(const char *fileName)
return dot;
}
// String pointer reverse break: returns right-most occurrence of charset in s
static const char *strprbrk(const char *s, const char *charset)
// String pointer reverse break: returns right-most occurrence of charset in text
static const char *strprbrk(const char *text, const char *charset)
{
const char *latestMatch = NULL;
for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
return latestMatch;
}
@ -2768,11 +2768,11 @@ static const char *GetFileNameWithoutExt(const char *filePath)
static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
if (filePath != NULL) strcpy(fileName, GetFileName(filePath)); // Get filename with extension
if (filePath != NULL) strncpy(fileName, GetFileName(filePath), MAX_FILENAMEWITHOUTEXT_LENGTH - 1); // Get filename with extension
int size = (int)strlen(fileName); // Get size in bytes
int fileNameLength = (int)strlen(fileName); // Get size in bytes
for (int i = 0; (i < size) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
for (int i = 0; (i < fileNameLength) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
{
if (fileName[i] == '.')
{
@ -2866,7 +2866,8 @@ static bool SaveFileText(const char *fileName, char *text)
if (file != NULL)
{
int count = fprintf(file, "%s", text);
int count = 0;
if (text != NULL) count = fprintf(file, "%s", text);
if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);

View file

@ -33,7 +33,6 @@
* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
*
* OPTIONAL DEPENDENCIES (included):
* [rcore] msf_gif (Miles Fogle) for GIF recording
* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm
* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm
* [rcore] rprand (Ramon Santamaria) for pseudo-random numbers generation
@ -100,13 +99,13 @@
#define __declspec(x) __attribute__((x))
#endif
#if defined(BUILD_LIBTYPE_SHARED)
#define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll)
#define RLAPI __declspec(dllexport) // Building the library as a Win32 shared library (.dll)
#elif defined(USE_LIBTYPE_SHARED)
#define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll)
#define RLAPI __declspec(dllimport) // Using the library as a Win32 shared library (.dll)
#endif
#else
#if defined(BUILD_LIBTYPE_SHARED)
#define RLAPI __attribute__((visibility("default"))) // We are building as a Unix shared library (.so/.dylib)
#define RLAPI __attribute__((visibility("default"))) // Building as a Unix shared library (.so/.dylib)
#endif
#endif
@ -158,7 +157,7 @@
#error "C++11 or later is required. Add -std=c++11"
#endif
// NOTE: We set some defines with some data types declared by raylib
// NOTE: Set some defines with some data types declared by raylib
// Other modules (raymath, rlgl) also require some of those types, so,
// to be able to use those other modules as standalone (not depending on raylib)
// this defines are very useful for internal check and avoid type (re)definitions
@ -335,10 +334,10 @@ typedef Camera3D Camera; // Camera type fallback, defaults to Camera3D
// Camera2D, defines position/orientation in 2d space
typedef struct Camera2D {
Vector2 offset; // Camera offset (displacement from target)
Vector2 target; // Camera target (rotation and zoom origin)
float rotation; // Camera rotation in degrees
float zoom; // Camera zoom (scaling), should be 1.0f by default
Vector2 offset; // Camera offset (screen space offset from window origin)
Vector2 target; // Camera target (world space target point that is mapped to screen space offset)
float rotation; // Camera rotation in degrees (pivots around target)
float zoom; // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale
} Camera2D;
// Mesh, vertex data and vao/vbo
@ -1146,7 +1145,7 @@ RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previ
RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string)
RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string)
RLAPI int MakeDirectory(const char *dirPath); // Create directories (including full path requested), returns 0 on success
RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success
RLAPI bool ChangeDirectory(const char *dirPath); // Change working directory, return true on success
RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory
RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS
RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths
@ -1658,7 +1657,7 @@ RLAPI Sound LoadSound(const char *fileName); // Load so
RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data
RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data
RLAPI bool IsSoundValid(Sound sound); // Checks if a sound is valid (data loaded and buffers initialized)
RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data (data and frame count should fit in sound)
RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data (default data format: 32 bit float, stereo)
RLAPI void UnloadWave(Wave wave); // Unload wave data
RLAPI void UnloadSound(Sound sound); // Unload sound
RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data)
@ -1673,7 +1672,7 @@ RLAPI void ResumeSound(Sound sound); // Resume
RLAPI bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing
RLAPI void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level)
RLAPI void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level)
RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (0.5 is center)
RLAPI void SetSoundPan(Sound sound, float pan); // Set pan for a sound (-1.0 left, 0.0 center, 1.0 right)
RLAPI Wave WaveCopy(Wave wave); // Copy a wave to a new wave
RLAPI void WaveCrop(Wave *wave, int initFrame, int finalFrame); // Crop a wave to defined frames range
RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format
@ -1694,7 +1693,7 @@ RLAPI void ResumeMusicStream(Music music); // Resume
RLAPI void SeekMusicStream(Music music, float position); // Seek music to a position (in seconds)
RLAPI void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level)
RLAPI void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level)
RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (0.5 is center)
RLAPI void SetMusicPan(Music music, float pan); // Set pan for a music (-1.0 left, 0.0 center, 1.0 right)
RLAPI float GetMusicTimeLength(Music music); // Get music time length (in seconds)
RLAPI float GetMusicTimePlayed(Music music); // Get current music time played (in seconds)

View file

@ -2552,7 +2552,38 @@ RMAPI int QuaternionEquals(Quaternion p, Quaternion q)
return result;
}
// Compose a transformation matrix from rotational, translational and scaling components
// TODO: This function is not following raymath conventions defined in header: NOT self-contained
RMAPI Matrix MatrixCompose(Vector3 translation, Quaternion rotation, Vector3 scale)
{
// Initialize vectors
Vector3 right = { 1.0f, 0.0f, 0.0f };
Vector3 up = { 0.0f, 1.0f, 0.0f };
Vector3 forward = { 0.0f, 0.0f, 1.0f };
// Scale vectors
right = Vector3Scale(right, scale.x);
up = Vector3Scale(up, scale.y);
forward = Vector3Scale(forward , scale.z);
// Rotate vectors
right = Vector3RotateByQuaternion(right, rotation);
up = Vector3RotateByQuaternion(up, rotation);
forward = Vector3RotateByQuaternion(forward, rotation);
// Set result matrix output
Matrix result = {
right.x, up.x, forward.x, translation.x,
right.y, up.y, forward.y, translation.y,
right.z, up.z, forward.z, translation.z,
0.0f, 0.0f, 0.0f, 1.0f
};
return result;
}
// Decompose a transformation matrix into its rotational, translational and scaling components and remove shear
// TODO: This function is not following raymath conventions defined in header: NOT self-contained
RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotation, Vector3 *scale)
{
float eps = (float)1e-9;
@ -2587,10 +2618,7 @@ RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotatio
// X Scale
scl.x = Vector3Length(matColumns[0]);
if (scl.x > eps)
{
matColumns[0] = Vector3Scale(matColumns[0], 1.0f / scl.x);
}
if (scl.x > eps) matColumns[0] = Vector3Scale(matColumns[0], 1.0f / scl.x);
// Compute XY shear and make col2 orthogonal
shear[0] = Vector3DotProduct(matColumns[0], matColumns[1]);

Some files were not shown because too many files have changed in this diff Show more