commit 6b6f8b8b5f96c459436640e56191c098b2924451 Author: Nigel Date: Sun Jan 11 16:10:52 2026 +0100 Initial window setup diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a89bc2d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.so.* filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..79e4acb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,16 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.20) +set( CMAKE_EXPORT_COMPILE_COMMANDS on) +project(Pacman) + +add_executable(Pacman src/main.c) +target_link_libraries( Pacman + X11 + GL + Xrandr + +) +target_include_directories(Pacman + PRIVATE "libs/rgfw/" + +) + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/libs/box2d/include/box2d/base.h b/libs/box2d/include/box2d/base.h new file mode 100644 index 0000000..26535d7 --- /dev/null +++ b/libs/box2d/include/box2d/base.h @@ -0,0 +1,139 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +// clang-format off +// +// Shared library macros +#if defined( _MSC_VER ) && defined( box2d_EXPORTS ) + // build the Windows DLL + #define BOX2D_EXPORT __declspec( dllexport ) +#elif defined( _MSC_VER ) && defined( BOX2D_DLL ) + // using the Windows DLL + #define BOX2D_EXPORT __declspec( dllimport ) +#elif defined( box2d_EXPORTS ) + // building or using the shared library + #define BOX2D_EXPORT __attribute__( ( visibility( "default" ) ) ) +#else + // static library + #define BOX2D_EXPORT +#endif + +// C++ macros +#ifdef __cplusplus + #define B2_API extern "C" BOX2D_EXPORT + #define B2_INLINE inline + #define B2_LITERAL(T) T + #define B2_ZERO_INIT {} +#else + #define B2_API BOX2D_EXPORT + #define B2_INLINE static inline + /// Used for C literals like (b2Vec2){1.0f, 2.0f} where C++ requires b2Vec2{1.0f, 2.0f} + #define B2_LITERAL(T) (T) + #define B2_ZERO_INIT {0} +#endif +// clang-format on + +/** + * @defgroup base Base + * Base functionality + * @{ + */ + +/// Prototype for user allocation function +/// @param size the allocation size in bytes +/// @param alignment the required alignment, guaranteed to be a power of 2 +typedef void* b2AllocFcn( unsigned int size, int alignment ); + +/// Prototype for user free function +/// @param mem the memory previously allocated through `b2AllocFcn` +/// @param size the allocation size in bytes +typedef void b2FreeFcn( void* mem, unsigned int size ); + +/// Prototype for the user assert callback. Return 0 to skip the debugger break. +typedef int b2AssertFcn( const char* condition, const char* fileName, int lineNumber ); + +/// Prototype for user log callback. Used to log warnings. +typedef void b2LogFcn( const char* message ); + +/// This allows the user to override the allocation functions. These should be +/// set during application startup. +B2_API void b2SetAllocator( b2AllocFcn* allocFcn, b2FreeFcn* freeFcn ); + +/// @return the total bytes allocated by Box2D +B2_API int b2GetByteCount( void ); + +/// Override the default assert function +/// @param assertFcn a non-null assert callback +B2_API void b2SetAssertFcn( b2AssertFcn* assertFcn ); + +/// Override the default log function +/// @param logFcn a non-null log callback +B2_API void b2SetLogFcn( b2LogFcn* logFcn ); + +/// Version numbering scheme. +/// See https://semver.org/ +typedef struct b2Version +{ + /// Significant changes + int major; + + /// Incremental changes + int minor; + + /// Bug fixes + int revision; +} b2Version; + +/// Get the current version of Box2D +B2_API b2Version b2GetVersion( void ); + +/**@}*/ + +//! @cond + +// see https://github.com/scottt/debugbreak +#if defined( _MSC_VER ) +#define B2_BREAKPOINT __debugbreak() +#elif defined( __GNUC__ ) || defined( __clang__ ) +#define B2_BREAKPOINT __builtin_trap() +#else +// Unknown compiler +#include +#define B2_BREAKPOINT assert( 0 ) +#endif + +#if !defined( NDEBUG ) || defined( B2_ENABLE_ASSERT ) +B2_API int b2InternalAssertFcn( const char* condition, const char* fileName, int lineNumber ); +#define B2_ASSERT( condition ) \ + do \ + { \ + if ( !( condition ) && b2InternalAssertFcn( #condition, __FILE__, (int)__LINE__ ) ) \ + B2_BREAKPOINT; \ + } \ + while ( 0 ) +#else +#define B2_ASSERT( ... ) ( (void)0 ) +#endif + +/// Get the absolute number of system ticks. The value is platform specific. +B2_API uint64_t b2GetTicks( void ); + +/// Get the milliseconds passed from an initial tick value. +B2_API float b2GetMilliseconds( uint64_t ticks ); + +/// Get the milliseconds passed from an initial tick value. Resets the passed in +/// value to the current tick value. +B2_API float b2GetMillisecondsAndReset( uint64_t* ticks ); + +/// Yield to be used in a busy loop. +B2_API void b2Yield( void ); + +/// Simple djb2 hash function for determinism testing +#define B2_HASH_INIT 5381 +B2_API uint32_t b2Hash( uint32_t hash, const uint8_t* data, int count ); + +//! @endcond diff --git a/libs/box2d/include/box2d/box2d.h b/libs/box2d/include/box2d/box2d.h new file mode 100644 index 0000000..f68d209 --- /dev/null +++ b/libs/box2d/include/box2d/box2d.h @@ -0,0 +1,1286 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" +#include "collision.h" +#include "id.h" +#include "types.h" + +#include + +/** + * @defgroup world World + * These functions allow you to create a simulation world. + * + * You can add rigid bodies and joint constraints to the world and run the simulation. You can get contact + * information to get contact points and normals as well as events. You can query to world, checking for overlaps and casting rays + * or shapes. There is also debugging information such as debug draw, timing information, and counters. You can find documentation + * here: https://box2d.org/ + * @{ + */ + +/// Create a world for rigid body simulation. A world contains bodies, shapes, and constraints. You make create +/// up to 128 worlds. Each world is completely independent and may be simulated in parallel. +/// @return the world id. +B2_API b2WorldId b2CreateWorld( const b2WorldDef* def ); + +/// Destroy a world +B2_API void b2DestroyWorld( b2WorldId worldId ); + +/// World id validation. Provides validation for up to 64K allocations. +B2_API bool b2World_IsValid( b2WorldId id ); + +/// Simulate a world for one time step. This performs collision detection, integration, and constraint solution. +/// @param worldId The world to simulate +/// @param timeStep The amount of time to simulate, this should be a fixed number. Usually 1/60. +/// @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Usually 4. +B2_API void b2World_Step( b2WorldId worldId, float timeStep, int subStepCount ); + +/// Call this to draw shapes and other debug draw data +B2_API void b2World_Draw( b2WorldId worldId, b2DebugDraw* draw ); + +/// Get the body events for the current time step. The event data is transient. Do not store a reference to this data. +B2_API b2BodyEvents b2World_GetBodyEvents( b2WorldId worldId ); + +/// Get sensor events for the current time step. The event data is transient. Do not store a reference to this data. +B2_API b2SensorEvents b2World_GetSensorEvents( b2WorldId worldId ); + +/// Get contact events for this current time step. The event data is transient. Do not store a reference to this data. +B2_API b2ContactEvents b2World_GetContactEvents( b2WorldId worldId ); + +/// Get the joint events for the current time step. The event data is transient. Do not store a reference to this data. +B2_API b2JointEvents b2World_GetJointEvents( b2WorldId worldId ); + +/// Overlap test for all shapes that *potentially* overlap the provided AABB +B2_API b2TreeStats b2World_OverlapAABB( b2WorldId worldId, b2AABB aabb, b2QueryFilter filter, b2OverlapResultFcn* fcn, + void* context ); + +/// Overlap test for all shapes that overlap the provided shape proxy. +B2_API b2TreeStats b2World_OverlapShape( b2WorldId worldId, const b2ShapeProxy* proxy, b2QueryFilter filter, + b2OverlapResultFcn* fcn, void* context ); + +/// Cast a ray into the world to collect shapes in the path of the ray. +/// Your callback function controls whether you get the closest point, any point, or n-points. +/// @note The callback function may receive shapes in any order +/// @param worldId The world to cast the ray against +/// @param origin The start point of the ray +/// @param translation The translation of the ray from the start point to the end point +/// @param filter Contains bit flags to filter unwanted shapes from the results +/// @param fcn A user implemented callback function +/// @param context A user context that is passed along to the callback function +/// @return traversal performance counters +B2_API b2TreeStats b2World_CastRay( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter, + b2CastResultFcn* fcn, void* context ); + +/// Cast a ray into the world to collect the closest hit. This is a convenience function. Ignores initial overlap. +/// This is less general than b2World_CastRay() and does not allow for custom filtering. +B2_API b2RayResult b2World_CastRayClosest( b2WorldId worldId, b2Vec2 origin, b2Vec2 translation, b2QueryFilter filter ); + +/// Cast a shape through the world. Similar to a cast ray except that a shape is cast instead of a point. +/// @see b2World_CastRay +B2_API b2TreeStats b2World_CastShape( b2WorldId worldId, const b2ShapeProxy* proxy, b2Vec2 translation, b2QueryFilter filter, + b2CastResultFcn* fcn, void* context ); + +/// Cast a capsule mover through the world. This is a special shape cast that handles sliding along other shapes while reducing +/// clipping. +B2_API float b2World_CastMover( b2WorldId worldId, const b2Capsule* mover, b2Vec2 translation, b2QueryFilter filter ); + +/// Collide a capsule mover with the world, gathering collision planes that can be fed to b2SolvePlanes. Useful for +/// kinematic character movement. +B2_API void b2World_CollideMover( b2WorldId worldId, const b2Capsule* mover, b2QueryFilter filter, b2PlaneResultFcn* fcn, + void* context ); + +/// Enable/disable sleep. If your application does not need sleeping, you can gain some performance +/// by disabling sleep completely at the world level. +/// @see b2WorldDef +B2_API void b2World_EnableSleeping( b2WorldId worldId, bool flag ); + +/// Is body sleeping enabled? +B2_API bool b2World_IsSleepingEnabled( b2WorldId worldId ); + +/// Enable/disable continuous collision between dynamic and static bodies. Generally you should keep continuous +/// collision enabled to prevent fast moving objects from going through static objects. The performance gain from +/// disabling continuous collision is minor. +/// @see b2WorldDef +B2_API void b2World_EnableContinuous( b2WorldId worldId, bool flag ); + +/// Is continuous collision enabled? +B2_API bool b2World_IsContinuousEnabled( b2WorldId worldId ); + +/// Adjust the restitution threshold. It is recommended not to make this value very small +/// because it will prevent bodies from sleeping. Usually in meters per second. +/// @see b2WorldDef +B2_API void b2World_SetRestitutionThreshold( b2WorldId worldId, float value ); + +/// Get the the restitution speed threshold. Usually in meters per second. +B2_API float b2World_GetRestitutionThreshold( b2WorldId worldId ); + +/// Adjust the hit event threshold. This controls the collision speed needed to generate a b2ContactHitEvent. +/// Usually in meters per second. +/// @see b2WorldDef::hitEventThreshold +B2_API void b2World_SetHitEventThreshold( b2WorldId worldId, float value ); + +/// Get the the hit event speed threshold. Usually in meters per second. +B2_API float b2World_GetHitEventThreshold( b2WorldId worldId ); + +/// Register the custom filter callback. This is optional. +B2_API void b2World_SetCustomFilterCallback( b2WorldId worldId, b2CustomFilterFcn* fcn, void* context ); + +/// Register the pre-solve callback. This is optional. +B2_API void b2World_SetPreSolveCallback( b2WorldId worldId, b2PreSolveFcn* fcn, void* context ); + +/// Set the gravity vector for the entire world. Box2D has no concept of an up direction and this +/// is left as a decision for the application. Usually in m/s^2. +/// @see b2WorldDef +B2_API void b2World_SetGravity( b2WorldId worldId, b2Vec2 gravity ); + +/// Get the gravity vector +B2_API b2Vec2 b2World_GetGravity( b2WorldId worldId ); + +/// Apply a radial explosion +/// @param worldId The world id +/// @param explosionDef The explosion definition +B2_API void b2World_Explode( b2WorldId worldId, const b2ExplosionDef* explosionDef ); + +/// Adjust contact tuning parameters +/// @param worldId The world id +/// @param hertz The contact stiffness (cycles per second) +/// @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional) +/// @param pushSpeed The maximum contact constraint push out speed (meters per second) +/// @note Advanced feature +B2_API void b2World_SetContactTuning( b2WorldId worldId, float hertz, float dampingRatio, float pushSpeed ); + +/// Set the maximum linear speed. Usually in m/s. +B2_API void b2World_SetMaximumLinearSpeed( b2WorldId worldId, float maximumLinearSpeed ); + +/// Get the maximum linear speed. Usually in m/s. +B2_API float b2World_GetMaximumLinearSpeed( b2WorldId worldId ); + +/// Enable/disable constraint warm starting. Advanced feature for testing. Disabling +/// warm starting greatly reduces stability and provides no performance gain. +B2_API void b2World_EnableWarmStarting( b2WorldId worldId, bool flag ); + +/// Is constraint warm starting enabled? +B2_API bool b2World_IsWarmStartingEnabled( b2WorldId worldId ); + +/// Get the number of awake bodies. +B2_API int b2World_GetAwakeBodyCount( b2WorldId worldId ); + +/// Get the current world performance profile +B2_API b2Profile b2World_GetProfile( b2WorldId worldId ); + +/// Get world counters and sizes +B2_API b2Counters b2World_GetCounters( b2WorldId worldId ); + +/// Set the user data pointer. +B2_API void b2World_SetUserData( b2WorldId worldId, void* userData ); + +/// Get the user data pointer. +B2_API void* b2World_GetUserData( b2WorldId worldId ); + +/// Set the friction callback. Passing NULL resets to default. +B2_API void b2World_SetFrictionCallback( b2WorldId worldId, b2FrictionCallback* callback ); + +/// Set the restitution callback. Passing NULL resets to default. +B2_API void b2World_SetRestitutionCallback( b2WorldId worldId, b2RestitutionCallback* callback ); + +/// Dump memory stats to box2d_memory.txt +B2_API void b2World_DumpMemoryStats( b2WorldId worldId ); + +/// This is for internal testing +B2_API void b2World_RebuildStaticTree( b2WorldId worldId ); + +/// This is for internal testing +B2_API void b2World_EnableSpeculative( b2WorldId worldId, bool flag ); + +/** @} */ + +/** + * @defgroup body Body + * This is the body API. + * @{ + */ + +/// Create a rigid body given a definition. No reference to the definition is retained. So you can create the definition +/// on the stack and pass it as a pointer. +/// @code{.c} +/// b2BodyDef bodyDef = b2DefaultBodyDef(); +/// b2BodyId myBodyId = b2CreateBody(myWorldId, &bodyDef); +/// @endcode +/// @warning This function is locked during callbacks. +B2_API b2BodyId b2CreateBody( b2WorldId worldId, const b2BodyDef* def ); + +/// Destroy a rigid body given an id. This destroys all shapes and joints attached to the body. +/// Do not keep references to the associated shapes and joints. +B2_API void b2DestroyBody( b2BodyId bodyId ); + +/// Body identifier validation. A valid body exists in a world and is non-null. +/// This can be used to detect orphaned ids. Provides validation for up to 64K allocations. +B2_API bool b2Body_IsValid( b2BodyId id ); + +/// Get the body type: static, kinematic, or dynamic +B2_API b2BodyType b2Body_GetType( b2BodyId bodyId ); + +/// Change the body type. This is an expensive operation. This automatically updates the mass +/// properties regardless of the automatic mass setting. +B2_API void b2Body_SetType( b2BodyId bodyId, b2BodyType type ); + +/// Set the body name. Up to 31 characters excluding 0 termination. +B2_API void b2Body_SetName( b2BodyId bodyId, const char* name ); + +/// Get the body name. +B2_API const char* b2Body_GetName( b2BodyId bodyId ); + +/// Set the user data for a body +B2_API void b2Body_SetUserData( b2BodyId bodyId, void* userData ); + +/// Get the user data stored in a body +B2_API void* b2Body_GetUserData( b2BodyId bodyId ); + +/// Get the world position of a body. This is the location of the body origin. +B2_API b2Vec2 b2Body_GetPosition( b2BodyId bodyId ); + +/// Get the world rotation of a body as a cosine/sine pair (complex number) +B2_API b2Rot b2Body_GetRotation( b2BodyId bodyId ); + +/// Get the world transform of a body. +B2_API b2Transform b2Body_GetTransform( b2BodyId bodyId ); + +/// Set the world transform of a body. This acts as a teleport and is fairly expensive. +/// @note Generally you should create a body with then intended transform. +/// @see b2BodyDef::position and b2BodyDef::rotation +B2_API void b2Body_SetTransform( b2BodyId bodyId, b2Vec2 position, b2Rot rotation ); + +/// Get a local point on a body given a world point +B2_API b2Vec2 b2Body_GetLocalPoint( b2BodyId bodyId, b2Vec2 worldPoint ); + +/// Get a world point on a body given a local point +B2_API b2Vec2 b2Body_GetWorldPoint( b2BodyId bodyId, b2Vec2 localPoint ); + +/// Get a local vector on a body given a world vector +B2_API b2Vec2 b2Body_GetLocalVector( b2BodyId bodyId, b2Vec2 worldVector ); + +/// Get a world vector on a body given a local vector +B2_API b2Vec2 b2Body_GetWorldVector( b2BodyId bodyId, b2Vec2 localVector ); + +/// Get the linear velocity of a body's center of mass. Usually in meters per second. +B2_API b2Vec2 b2Body_GetLinearVelocity( b2BodyId bodyId ); + +/// Get the angular velocity of a body in radians per second +B2_API float b2Body_GetAngularVelocity( b2BodyId bodyId ); + +/// Set the linear velocity of a body. Usually in meters per second. +B2_API void b2Body_SetLinearVelocity( b2BodyId bodyId, b2Vec2 linearVelocity ); + +/// Set the angular velocity of a body in radians per second +B2_API void b2Body_SetAngularVelocity( b2BodyId bodyId, float angularVelocity ); + +/// Set the velocity to reach the given transform after a given time step. +/// The result will be close but maybe not exact. This is meant for kinematic bodies. +/// The target is not applied if the velocity would be below the sleep threshold and +/// the body is currently asleep. +/// @param bodyId The body id +/// @param target The target transform for the body +/// @param timeStep The time step of the next call to b2World_Step +/// @param wake Option to wake the body or not +B2_API void b2Body_SetTargetTransform( b2BodyId bodyId, b2Transform target, float timeStep, bool wake ); + +/// Get the linear velocity of a local point attached to a body. Usually in meters per second. +B2_API b2Vec2 b2Body_GetLocalPointVelocity( b2BodyId bodyId, b2Vec2 localPoint ); + +/// Get the linear velocity of a world point attached to a body. Usually in meters per second. +B2_API b2Vec2 b2Body_GetWorldPointVelocity( b2BodyId bodyId, b2Vec2 worldPoint ); + +/// Apply a force at a world point. If the force is not applied at the center of mass, +/// it will generate a torque and affect the angular velocity. This optionally wakes up the body. +/// The force is ignored if the body is not awake. +/// @param bodyId The body id +/// @param force The world force vector, usually in newtons (N) +/// @param point The world position of the point of application +/// @param wake Option to wake up the body +B2_API void b2Body_ApplyForce( b2BodyId bodyId, b2Vec2 force, b2Vec2 point, bool wake ); + +/// Apply a force to the center of mass. This optionally wakes up the body. +/// The force is ignored if the body is not awake. +/// @param bodyId The body id +/// @param force the world force vector, usually in newtons (N). +/// @param wake also wake up the body +B2_API void b2Body_ApplyForceToCenter( b2BodyId bodyId, b2Vec2 force, bool wake ); + +/// Apply a torque. This affects the angular velocity without affecting the linear velocity. +/// This optionally wakes the body. The torque is ignored if the body is not awake. +/// @param bodyId The body id +/// @param torque about the z-axis (out of the screen), usually in N*m. +/// @param wake also wake up the body +B2_API void b2Body_ApplyTorque( b2BodyId bodyId, float torque, bool wake ); + +/// Clear the force and torque on this body. Forces and torques are automatically cleared after each world +/// step. So this only needs to be called if the application wants to remove the effect of previous +/// calls to apply forces and torques before the world step is called. +/// @param bodyId The body id +B2_API void b2Body_ClearForces( b2BodyId bodyId ); + +/// Apply an impulse at a point. This immediately modifies the velocity. +/// It also modifies the angular velocity if the point of application +/// is not at the center of mass. This optionally wakes the body. +/// The impulse is ignored if the body is not awake. +/// @param bodyId The body id +/// @param impulse the world impulse vector, usually in N*s or kg*m/s. +/// @param point the world position of the point of application. +/// @param wake also wake up the body +/// @warning This should be used for one-shot impulses. If you need a steady force, +/// use a force instead, which will work better with the sub-stepping solver. +B2_API void b2Body_ApplyLinearImpulse( b2BodyId bodyId, b2Vec2 impulse, b2Vec2 point, bool wake ); + +/// Apply an impulse to the center of mass. This immediately modifies the velocity. +/// The impulse is ignored if the body is not awake. This optionally wakes the body. +/// @param bodyId The body id +/// @param impulse the world impulse vector, usually in N*s or kg*m/s. +/// @param wake also wake up the body +/// @warning This should be used for one-shot impulses. If you need a steady force, +/// use a force instead, which will work better with the sub-stepping solver. +B2_API void b2Body_ApplyLinearImpulseToCenter( b2BodyId bodyId, b2Vec2 impulse, bool wake ); + +/// Apply an angular impulse. The impulse is ignored if the body is not awake. +/// This optionally wakes the body. +/// @param bodyId The body id +/// @param impulse the angular impulse, usually in units of kg*m*m/s +/// @param wake also wake up the body +/// @warning This should be used for one-shot impulses. If you need a steady torque, +/// use a torque instead, which will work better with the sub-stepping solver. +B2_API void b2Body_ApplyAngularImpulse( b2BodyId bodyId, float impulse, bool wake ); + +/// Get the mass of the body, usually in kilograms +B2_API float b2Body_GetMass( b2BodyId bodyId ); + +/// Get the rotational inertia of the body, usually in kg*m^2 +B2_API float b2Body_GetRotationalInertia( b2BodyId bodyId ); + +/// Get the center of mass position of the body in local space +B2_API b2Vec2 b2Body_GetLocalCenterOfMass( b2BodyId bodyId ); + +/// Get the center of mass position of the body in world space +B2_API b2Vec2 b2Body_GetWorldCenterOfMass( b2BodyId bodyId ); + +/// Override the body's mass properties. Normally this is computed automatically using the +/// shape geometry and density. This information is lost if a shape is added or removed or if the +/// body type changes. +B2_API void b2Body_SetMassData( b2BodyId bodyId, b2MassData massData ); + +/// Get the mass data for a body +B2_API b2MassData b2Body_GetMassData( b2BodyId bodyId ); + +/// This updates the mass properties to the sum of the mass properties of the shapes. +/// This normally does not need to be called unless you called SetMassData to override +/// the mass and you later want to reset the mass. +/// You may also use this when automatic mass computation has been disabled. +/// You should call this regardless of body type. +/// Note that sensor shapes may have mass. +B2_API void b2Body_ApplyMassFromShapes( b2BodyId bodyId ); + +/// Adjust the linear damping. Normally this is set in b2BodyDef before creation. +B2_API void b2Body_SetLinearDamping( b2BodyId bodyId, float linearDamping ); + +/// Get the current linear damping. +B2_API float b2Body_GetLinearDamping( b2BodyId bodyId ); + +/// Adjust the angular damping. Normally this is set in b2BodyDef before creation. +B2_API void b2Body_SetAngularDamping( b2BodyId bodyId, float angularDamping ); + +/// Get the current angular damping. +B2_API float b2Body_GetAngularDamping( b2BodyId bodyId ); + +/// Adjust the gravity scale. Normally this is set in b2BodyDef before creation. +/// @see b2BodyDef::gravityScale +B2_API void b2Body_SetGravityScale( b2BodyId bodyId, float gravityScale ); + +/// Get the current gravity scale +B2_API float b2Body_GetGravityScale( b2BodyId bodyId ); + +/// @return true if this body is awake +B2_API bool b2Body_IsAwake( b2BodyId bodyId ); + +/// Wake a body from sleep. This wakes the entire island the body is touching. +/// @warning Putting a body to sleep will put the entire island of bodies touching this body to sleep, +/// which can be expensive and possibly unintuitive. +B2_API void b2Body_SetAwake( b2BodyId bodyId, bool awake ); + +/// Wake bodies touching this body. Works for static bodies. +B2_API void b2Body_WakeTouching( b2BodyId bodyId ); + +/// Enable or disable sleeping for this body. If sleeping is disabled the body will wake. +B2_API void b2Body_EnableSleep( b2BodyId bodyId, bool enableSleep ); + +/// Returns true if sleeping is enabled for this body +B2_API bool b2Body_IsSleepEnabled( b2BodyId bodyId ); + +/// Set the sleep threshold, usually in meters per second +B2_API void b2Body_SetSleepThreshold( b2BodyId bodyId, float sleepThreshold ); + +/// Get the sleep threshold, usually in meters per second. +B2_API float b2Body_GetSleepThreshold( b2BodyId bodyId ); + +/// Returns true if this body is enabled +B2_API bool b2Body_IsEnabled( b2BodyId bodyId ); + +/// Disable a body by removing it completely from the simulation. This is expensive. +B2_API void b2Body_Disable( b2BodyId bodyId ); + +/// Enable a body by adding it to the simulation. This is expensive. +B2_API void b2Body_Enable( b2BodyId bodyId ); + +/// Set the motion locks on this body. +B2_API void b2Body_SetMotionLocks( b2BodyId bodyId, b2MotionLocks locks ); + +/// Get the motion locks for this body. +B2_API b2MotionLocks b2Body_GetMotionLocks( b2BodyId bodyId ); + +/// Set this body to be a bullet. A bullet does continuous collision detection +/// against dynamic bodies (but not other bullets). +B2_API void b2Body_SetBullet( b2BodyId bodyId, bool flag ); + +/// Is this body a bullet? +B2_API bool b2Body_IsBullet( b2BodyId bodyId ); + +/// Enable/disable contact events on all shapes. +/// @see b2ShapeDef::enableContactEvents +/// @warning changing this at runtime may cause mismatched begin/end touch events +B2_API void b2Body_EnableContactEvents( b2BodyId bodyId, bool flag ); + +/// Enable/disable hit events on all shapes +/// @see b2ShapeDef::enableHitEvents +B2_API void b2Body_EnableHitEvents( b2BodyId bodyId, bool flag ); + +/// Get the world that owns this body +B2_API b2WorldId b2Body_GetWorld( b2BodyId bodyId ); + +/// Get the number of shapes on this body +B2_API int b2Body_GetShapeCount( b2BodyId bodyId ); + +/// Get the shape ids for all shapes on this body, up to the provided capacity. +/// @returns the number of shape ids stored in the user array +B2_API int b2Body_GetShapes( b2BodyId bodyId, b2ShapeId* shapeArray, int capacity ); + +/// Get the number of joints on this body +B2_API int b2Body_GetJointCount( b2BodyId bodyId ); + +/// Get the joint ids for all joints on this body, up to the provided capacity +/// @returns the number of joint ids stored in the user array +B2_API int b2Body_GetJoints( b2BodyId bodyId, b2JointId* jointArray, int capacity ); + +/// Get the maximum capacity required for retrieving all the touching contacts on a body +B2_API int b2Body_GetContactCapacity( b2BodyId bodyId ); + +/// Get the touching contact data for a body. +/// @note Box2D uses speculative collision so some contact points may be separated. +/// @returns the number of elements filled in the provided array +/// @warning do not ignore the return value, it specifies the valid number of elements +B2_API int b2Body_GetContactData( b2BodyId bodyId, b2ContactData* contactData, int capacity ); + +/// Get the current world AABB that contains all the attached shapes. Note that this may not encompass the body origin. +/// If there are no shapes attached then the returned AABB is empty and centered on the body origin. +B2_API b2AABB b2Body_ComputeAABB( b2BodyId bodyId ); + +/** @} */ + +/** + * @defgroup shape Shape + * Functions to create, destroy, and access. + * Shapes bind raw geometry to bodies and hold material properties including friction and restitution. + * @{ + */ + +/// Create a circle shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B2_API b2ShapeId b2CreateCircleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Circle* circle ); + +/// Create a line segment shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B2_API b2ShapeId b2CreateSegmentShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Segment* segment ); + +/// Create a capsule shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape, this will be b2_nullShapeId if the length is too small. +B2_API b2ShapeId b2CreateCapsuleShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Capsule* capsule ); + +/// Create a polygon shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B2_API b2ShapeId b2CreatePolygonShape( b2BodyId bodyId, const b2ShapeDef* def, const b2Polygon* polygon ); + +/// Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a +/// body are destroyed at once. +/// @see b2Body_ApplyMassFromShapes +B2_API void b2DestroyShape( b2ShapeId shapeId, bool updateBodyMass ); + +/// Shape identifier validation. Provides validation for up to 64K allocations. +B2_API bool b2Shape_IsValid( b2ShapeId id ); + +/// Get the type of a shape +B2_API b2ShapeType b2Shape_GetType( b2ShapeId shapeId ); + +/// Get the id of the body that a shape is attached to +B2_API b2BodyId b2Shape_GetBody( b2ShapeId shapeId ); + +/// Get the world that owns this shape +B2_API b2WorldId b2Shape_GetWorld( b2ShapeId shapeId ); + +/// Returns true if the shape is a sensor. It is not possible to change a shape +/// from sensor to solid dynamically because this breaks the contract for +/// sensor events. +B2_API bool b2Shape_IsSensor( b2ShapeId shapeId ); + +/// Set the user data for a shape +B2_API void b2Shape_SetUserData( b2ShapeId shapeId, void* userData ); + +/// Get the user data for a shape. This is useful when you get a shape id +/// from an event or query. +B2_API void* b2Shape_GetUserData( b2ShapeId shapeId ); + +/// Set the mass density of a shape, usually in kg/m^2. +/// This will optionally update the mass properties on the parent body. +/// @see b2ShapeDef::density, b2Body_ApplyMassFromShapes +B2_API void b2Shape_SetDensity( b2ShapeId shapeId, float density, bool updateBodyMass ); + +/// Get the density of a shape, usually in kg/m^2 +B2_API float b2Shape_GetDensity( b2ShapeId shapeId ); + +/// Set the friction on a shape +B2_API void b2Shape_SetFriction( b2ShapeId shapeId, float friction ); + +/// Get the friction of a shape +B2_API float b2Shape_GetFriction( b2ShapeId shapeId ); + +/// Set the shape restitution (bounciness) +B2_API void b2Shape_SetRestitution( b2ShapeId shapeId, float restitution ); + +/// Get the shape restitution +B2_API float b2Shape_GetRestitution( b2ShapeId shapeId ); + +/// Set the user material identifier +B2_API void b2Shape_SetUserMaterial( b2ShapeId shapeId, uint64_t material ); + +/// Get the user material identifier +B2_API uint64_t b2Shape_GetUserMaterial( b2ShapeId shapeId ); + +/// Set the shape surface material +B2_API void b2Shape_SetSurfaceMaterial( b2ShapeId shapeId, const b2SurfaceMaterial* surfaceMaterial ); + +/// Get the shape surface material +B2_API b2SurfaceMaterial b2Shape_GetSurfaceMaterial( b2ShapeId shapeId ); + +/// Get the shape filter +B2_API b2Filter b2Shape_GetFilter( b2ShapeId shapeId ); + +/// Set the current filter. This is almost as expensive as recreating the shape. This may cause +/// contacts to be immediately destroyed. However contacts are not created until the next world step. +/// Sensor overlap state is also not updated until the next world step. +/// @see b2ShapeDef::filter +B2_API void b2Shape_SetFilter( b2ShapeId shapeId, b2Filter filter ); + +/// Enable sensor events for this shape. +/// @see b2ShapeDef::enableSensorEvents +B2_API void b2Shape_EnableSensorEvents( b2ShapeId shapeId, bool flag ); + +/// Returns true if sensor events are enabled. +B2_API bool b2Shape_AreSensorEventsEnabled( b2ShapeId shapeId ); + +/// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. +/// @see b2ShapeDef::enableContactEvents +/// @warning changing this at run-time may lead to lost begin/end events +B2_API void b2Shape_EnableContactEvents( b2ShapeId shapeId, bool flag ); + +/// Returns true if contact events are enabled +B2_API bool b2Shape_AreContactEventsEnabled( b2ShapeId shapeId ); + +/// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive +/// and must be carefully handled due to multithreading. Ignored for sensors. +/// @see b2PreSolveFcn +B2_API void b2Shape_EnablePreSolveEvents( b2ShapeId shapeId, bool flag ); + +/// Returns true if pre-solve events are enabled +B2_API bool b2Shape_ArePreSolveEventsEnabled( b2ShapeId shapeId ); + +/// Enable contact hit events for this shape. Ignored for sensors. +/// @see b2WorldDef.hitEventThreshold +B2_API void b2Shape_EnableHitEvents( b2ShapeId shapeId, bool flag ); + +/// Returns true if hit events are enabled +B2_API bool b2Shape_AreHitEventsEnabled( b2ShapeId shapeId ); + +/// Test a point for overlap with a shape +B2_API bool b2Shape_TestPoint( b2ShapeId shapeId, b2Vec2 point ); + +/// Ray cast a shape directly +B2_API b2CastOutput b2Shape_RayCast( b2ShapeId shapeId, const b2RayCastInput* input ); + +/// Get a copy of the shape's circle. Asserts the type is correct. +B2_API b2Circle b2Shape_GetCircle( b2ShapeId shapeId ); + +/// Get a copy of the shape's line segment. Asserts the type is correct. +B2_API b2Segment b2Shape_GetSegment( b2ShapeId shapeId ); + +/// Get a copy of the shape's chain segment. These come from chain shapes. +/// Asserts the type is correct. +B2_API b2ChainSegment b2Shape_GetChainSegment( b2ShapeId shapeId ); + +/// Get a copy of the shape's capsule. Asserts the type is correct. +B2_API b2Capsule b2Shape_GetCapsule( b2ShapeId shapeId ); + +/// Get a copy of the shape's convex polygon. Asserts the type is correct. +B2_API b2Polygon b2Shape_GetPolygon( b2ShapeId shapeId ); + +/// Allows you to change a shape to be a circle or update the current circle. +/// This does not modify the mass properties. +/// @see b2Body_ApplyMassFromShapes +B2_API void b2Shape_SetCircle( b2ShapeId shapeId, const b2Circle* circle ); + +/// Allows you to change a shape to be a capsule or update the current capsule. +/// This does not modify the mass properties. +/// @see b2Body_ApplyMassFromShapes +B2_API void b2Shape_SetCapsule( b2ShapeId shapeId, const b2Capsule* capsule ); + +/// Allows you to change a shape to be a segment or update the current segment. +B2_API void b2Shape_SetSegment( b2ShapeId shapeId, const b2Segment* segment ); + +/// Allows you to change a shape to be a polygon or update the current polygon. +/// This does not modify the mass properties. +/// @see b2Body_ApplyMassFromShapes +B2_API void b2Shape_SetPolygon( b2ShapeId shapeId, const b2Polygon* polygon ); + +/// Get the parent chain id if the shape type is a chain segment, otherwise +/// returns b2_nullChainId. +B2_API b2ChainId b2Shape_GetParentChain( b2ShapeId shapeId ); + +/// Get the maximum capacity required for retrieving all the touching contacts on a shape +B2_API int b2Shape_GetContactCapacity( b2ShapeId shapeId ); + +/// Get the touching contact data for a shape. The provided shapeId will be either shapeIdA or shapeIdB on the contact data. +/// @note Box2D uses speculative collision so some contact points may be separated. +/// @returns the number of elements filled in the provided array +/// @warning do not ignore the return value, it specifies the valid number of elements +B2_API int b2Shape_GetContactData( b2ShapeId shapeId, b2ContactData* contactData, int capacity ); + +/// Get the maximum capacity required for retrieving all the overlapped shapes on a sensor shape. +/// This returns 0 if the provided shape is not a sensor. +/// @param shapeId the id of a sensor shape +/// @returns the required capacity to get all the overlaps in b2Shape_GetSensorOverlaps +B2_API int b2Shape_GetSensorCapacity( b2ShapeId shapeId ); + +/// Get the overlap data for a sensor shape. +/// @param shapeId the id of a sensor shape +/// @param visitorIds a user allocated array that is filled with the overlapping shapes (visitors) +/// @param capacity the capacity of overlappedShapes +/// @returns the number of elements filled in the provided array +/// @warning do not ignore the return value, it specifies the valid number of elements +/// @warning overlaps may contain destroyed shapes so use b2Shape_IsValid to confirm each overlap +B2_API int b2Shape_GetSensorData( b2ShapeId shapeId, b2ShapeId* visitorIds, int capacity ); + +/// Get the current world AABB +B2_API b2AABB b2Shape_GetAABB( b2ShapeId shapeId ); + +/// Compute the mass data for a shape +B2_API b2MassData b2Shape_ComputeMassData( b2ShapeId shapeId ); + +/// Get the closest point on a shape to a target point. Target and result are in world space. +/// todo need sample +B2_API b2Vec2 b2Shape_GetClosestPoint( b2ShapeId shapeId, b2Vec2 target ); + +/// Apply a wind force to the body for this shape using the density of air. This considers +/// the projected area of the shape in the wind direction. This also considers +/// the relative velocity of the shape. +/// @param shapeId the shape id +/// @param wind the wind velocity in world space +/// @param drag the drag coefficient, the force that opposes the relative velocity +/// @param lift the lift coefficient, the force that is perpendicular to the relative velocity +/// @param wake should this wake the body +B2_API void b2Shape_ApplyWind( b2ShapeId shapeId, b2Vec2 wind, float drag, float lift, bool wake ); + +/// Chain Shape + +/// Create a chain shape +/// @see b2ChainDef for details +B2_API b2ChainId b2CreateChain( b2BodyId bodyId, const b2ChainDef* def ); + +/// Destroy a chain shape +B2_API void b2DestroyChain( b2ChainId chainId ); + +/// Get the world that owns this chain shape +B2_API b2WorldId b2Chain_GetWorld( b2ChainId chainId ); + +/// Get the number of segments on this chain +B2_API int b2Chain_GetSegmentCount( b2ChainId chainId ); + +/// Fill a user array with chain segment shape ids up to the specified capacity. Returns +/// the actual number of segments returned. +B2_API int b2Chain_GetSegments( b2ChainId chainId, b2ShapeId* segmentArray, int capacity ); + +/// Get the number of materials used on this chain. Must be 1 or the number of segments. +B2_API int b2Chain_GetSurfaceMaterialCount( b2ChainId chainId ); + +/// Set a chain material. If the chain has only one material, this material is applied to all +/// segments. Otherwise it is applied to a single segment. +B2_API void b2Chain_SetSurfaceMaterial( b2ChainId chainId, const b2SurfaceMaterial* material, int materialIndex ); + +/// Get a chain material by index. +B2_API b2SurfaceMaterial b2Chain_GetSurfaceMaterial( b2ChainId chainId, int materialIndex ); + +/// Chain identifier validation. Provides validation for up to 64K allocations. +B2_API bool b2Chain_IsValid( b2ChainId id ); + +/** @} */ + +/** + * @defgroup joint Joint + * @brief Joints allow you to connect rigid bodies together while allowing various forms of relative motions. + * @{ + */ + +/// Destroy a joint. Optionally wake attached bodies. +B2_API void b2DestroyJoint( b2JointId jointId, bool wakeAttached ); + +/// Joint identifier validation. Provides validation for up to 64K allocations. +B2_API bool b2Joint_IsValid( b2JointId id ); + +/// Get the joint type +B2_API b2JointType b2Joint_GetType( b2JointId jointId ); + +/// Get body A id on a joint +B2_API b2BodyId b2Joint_GetBodyA( b2JointId jointId ); + +/// Get body B id on a joint +B2_API b2BodyId b2Joint_GetBodyB( b2JointId jointId ); + +/// Get the world that owns this joint +B2_API b2WorldId b2Joint_GetWorld( b2JointId jointId ); + +/// Set the local frame on bodyA +B2_API void b2Joint_SetLocalFrameA( b2JointId jointId, b2Transform localFrame ); + +/// Get the local frame on bodyA +B2_API b2Transform b2Joint_GetLocalFrameA( b2JointId jointId ); + +/// Set the local frame on bodyB +B2_API void b2Joint_SetLocalFrameB( b2JointId jointId, b2Transform localFrame ); + +/// Get the local frame on bodyB +B2_API b2Transform b2Joint_GetLocalFrameB( b2JointId jointId ); + +/// Toggle collision between connected bodies +B2_API void b2Joint_SetCollideConnected( b2JointId jointId, bool shouldCollide ); + +/// Is collision allowed between connected bodies? +B2_API bool b2Joint_GetCollideConnected( b2JointId jointId ); + +/// Set the user data on a joint +B2_API void b2Joint_SetUserData( b2JointId jointId, void* userData ); + +/// Get the user data on a joint +B2_API void* b2Joint_GetUserData( b2JointId jointId ); + +/// Wake the bodies connect to this joint +B2_API void b2Joint_WakeBodies( b2JointId jointId ); + +/// Get the current constraint force for this joint. Usually in Newtons. +B2_API b2Vec2 b2Joint_GetConstraintForce( b2JointId jointId ); + +/// Get the current constraint torque for this joint. Usually in Newton * meters. +B2_API float b2Joint_GetConstraintTorque( b2JointId jointId ); + +/// Get the current linear separation error for this joint. Does not consider admissible movement. Usually in meters. +B2_API float b2Joint_GetLinearSeparation( b2JointId jointId ); + +/// Get the current angular separation error for this joint. Does not consider admissible movement. Usually in meters. +B2_API float b2Joint_GetAngularSeparation( b2JointId jointId ); + +/// Set the joint constraint tuning. Advanced feature. +/// @param jointId the joint +/// @param hertz the stiffness in Hertz (cycles per second) +/// @param dampingRatio the non-dimensional damping ratio (one for critical damping) +B2_API void b2Joint_SetConstraintTuning( b2JointId jointId, float hertz, float dampingRatio ); + +/// Get the joint constraint tuning. Advanced feature. +B2_API void b2Joint_GetConstraintTuning( b2JointId jointId, float* hertz, float* dampingRatio ); + +/// Set the force threshold for joint events (Newtons) +B2_API void b2Joint_SetForceThreshold( b2JointId jointId, float threshold ); + +/// Get the force threshold for joint events (Newtons) +B2_API float b2Joint_GetForceThreshold( b2JointId jointId ); + +/// Set the torque threshold for joint events (N-m) +B2_API void b2Joint_SetTorqueThreshold( b2JointId jointId, float threshold ); + +/// Get the torque threshold for joint events (N-m) +B2_API float b2Joint_GetTorqueThreshold( b2JointId jointId ); + +/** + * @defgroup distance_joint Distance Joint + * @brief Functions for the distance joint. + * @{ + */ + +/// Create a distance joint +/// @see b2DistanceJointDef for details +B2_API b2JointId b2CreateDistanceJoint( b2WorldId worldId, const b2DistanceJointDef* def ); + +/// Set the rest length of a distance joint +/// @param jointId The id for a distance joint +/// @param length The new distance joint length +B2_API void b2DistanceJoint_SetLength( b2JointId jointId, float length ); + +/// Get the rest length of a distance joint +B2_API float b2DistanceJoint_GetLength( b2JointId jointId ); + +/// Enable/disable the distance joint spring. When disabled the distance joint is rigid. +B2_API void b2DistanceJoint_EnableSpring( b2JointId jointId, bool enableSpring ); + +/// Is the distance joint spring enabled? +B2_API bool b2DistanceJoint_IsSpringEnabled( b2JointId jointId ); + +/// Set the force range for the spring. +B2_API void b2DistanceJoint_SetSpringForceRange( b2JointId jointId, float lowerForce, float upperForce ); + +/// Get the force range for the spring. +B2_API void b2DistanceJoint_GetSpringForceRange( b2JointId jointId, float* lowerForce, float* upperForce ); + +/// Set the spring stiffness in Hertz +B2_API void b2DistanceJoint_SetSpringHertz( b2JointId jointId, float hertz ); + +/// Set the spring damping ratio, non-dimensional +B2_API void b2DistanceJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the spring Hertz +B2_API float b2DistanceJoint_GetSpringHertz( b2JointId jointId ); + +/// Get the spring damping ratio +B2_API float b2DistanceJoint_GetSpringDampingRatio( b2JointId jointId ); + +/// Enable joint limit. The limit only works if the joint spring is enabled. Otherwise the joint is rigid +/// and the limit has no effect. +B2_API void b2DistanceJoint_EnableLimit( b2JointId jointId, bool enableLimit ); + +/// Is the distance joint limit enabled? +B2_API bool b2DistanceJoint_IsLimitEnabled( b2JointId jointId ); + +/// Set the minimum and maximum length parameters of a distance joint +B2_API void b2DistanceJoint_SetLengthRange( b2JointId jointId, float minLength, float maxLength ); + +/// Get the distance joint minimum length +B2_API float b2DistanceJoint_GetMinLength( b2JointId jointId ); + +/// Get the distance joint maximum length +B2_API float b2DistanceJoint_GetMaxLength( b2JointId jointId ); + +/// Get the current length of a distance joint +B2_API float b2DistanceJoint_GetCurrentLength( b2JointId jointId ); + +/// Enable/disable the distance joint motor +B2_API void b2DistanceJoint_EnableMotor( b2JointId jointId, bool enableMotor ); + +/// Is the distance joint motor enabled? +B2_API bool b2DistanceJoint_IsMotorEnabled( b2JointId jointId ); + +/// Set the distance joint motor speed, usually in meters per second +B2_API void b2DistanceJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); + +/// Get the distance joint motor speed, usually in meters per second +B2_API float b2DistanceJoint_GetMotorSpeed( b2JointId jointId ); + +/// Set the distance joint maximum motor force, usually in newtons +B2_API void b2DistanceJoint_SetMaxMotorForce( b2JointId jointId, float force ); + +/// Get the distance joint maximum motor force, usually in newtons +B2_API float b2DistanceJoint_GetMaxMotorForce( b2JointId jointId ); + +/// Get the distance joint current motor force, usually in newtons +B2_API float b2DistanceJoint_GetMotorForce( b2JointId jointId ); + +/** @} */ + +/** + * @defgroup motor_joint Motor Joint + * @brief Functions for the motor joint. + * + * The motor joint is designed to control the movement of a body while still being + * responsive to collisions. A spring controls the position and rotation. A velocity motor + * can be used to control velocity and allows for friction in top-down games. Both types + * of control can be combined. For example, you can have a spring with friction. + * Position and velocity control have force and torque limits. + * @{ + */ + +/// Create a motor joint +/// @see b2MotorJointDef for details +B2_API b2JointId b2CreateMotorJoint( b2WorldId worldId, const b2MotorJointDef* def ); + +/// Set the desired relative linear velocity in meters per second +B2_API void b2MotorJoint_SetLinearVelocity( b2JointId jointId, b2Vec2 velocity ); + +/// Get the desired relative linear velocity in meters per second +B2_API b2Vec2 b2MotorJoint_GetLinearVelocity( b2JointId jointId ); + +/// Set the desired relative angular velocity in radians per second +B2_API void b2MotorJoint_SetAngularVelocity( b2JointId jointId, float velocity ); + +/// Get the desired relative angular velocity in radians per second +B2_API float b2MotorJoint_GetAngularVelocity( b2JointId jointId ); + +/// Set the motor joint maximum force, usually in newtons +B2_API void b2MotorJoint_SetMaxVelocityForce( b2JointId jointId, float maxForce ); + +/// Get the motor joint maximum force, usually in newtons +B2_API float b2MotorJoint_GetMaxVelocityForce( b2JointId jointId ); + +/// Set the motor joint maximum torque, usually in newton-meters +B2_API void b2MotorJoint_SetMaxVelocityTorque( b2JointId jointId, float maxTorque ); + +/// Get the motor joint maximum torque, usually in newton-meters +B2_API float b2MotorJoint_GetMaxVelocityTorque( b2JointId jointId ); + +/// Set the spring linear hertz stiffness +B2_API void b2MotorJoint_SetLinearHertz( b2JointId jointId, float hertz ); + +/// Get the spring linear hertz stiffness +B2_API float b2MotorJoint_GetLinearHertz( b2JointId jointId ); + +/// Set the spring linear damping ratio. Use 1.0 for critical damping. +B2_API void b2MotorJoint_SetLinearDampingRatio( b2JointId jointId, float damping ); + +/// Get the spring linear damping ratio. +B2_API float b2MotorJoint_GetLinearDampingRatio( b2JointId jointId ); + +/// Set the spring angular hertz stiffness +B2_API void b2MotorJoint_SetAngularHertz( b2JointId jointId, float hertz ); + +/// Get the spring angular hertz stiffness +B2_API float b2MotorJoint_GetAngularHertz( b2JointId jointId ); + +/// Set the spring angular damping ratio. Use 1.0 for critical damping. +B2_API void b2MotorJoint_SetAngularDampingRatio( b2JointId jointId, float damping ); + +/// Get the spring angular damping ratio. +B2_API float b2MotorJoint_GetAngularDampingRatio( b2JointId jointId ); + +/// Set the maximum spring force in newtons. +B2_API void b2MotorJoint_SetMaxSpringForce( b2JointId jointId, float maxForce ); + +/// Get the maximum spring force in newtons. +B2_API float b2MotorJoint_GetMaxSpringForce( b2JointId jointId ); + +/// Set the maximum spring torque in newtons * meters +B2_API void b2MotorJoint_SetMaxSpringTorque( b2JointId jointId, float maxTorque ); + +/// Get the maximum spring torque in newtons * meters +B2_API float b2MotorJoint_GetMaxSpringTorque( b2JointId jointId ); + +/**@}*/ + +/** + * @defgroup filter_joint Filter Joint + * @brief Functions for the filter joint. + * + * The filter joint is used to disable collision between two bodies. As a side effect of being a joint, it also + * keeps the two bodies in the same simulation island. + * @{ + */ + +/// Create a filter joint. +/// @see b2FilterJointDef for details +B2_API b2JointId b2CreateFilterJoint( b2WorldId worldId, const b2FilterJointDef* def ); + +/**@}*/ + +/** + * @defgroup prismatic_joint Prismatic Joint + * @brief A prismatic joint allows for translation along a single axis with no rotation. + * + * The prismatic joint is useful for things like pistons and moving platforms, where you want a body to translate + * along an axis and have no rotation. Also called a *slider* joint. + * @{ + */ + +/// Create a prismatic (slider) joint. +/// @see b2PrismaticJointDef for details +B2_API b2JointId b2CreatePrismaticJoint( b2WorldId worldId, const b2PrismaticJointDef* def ); + +/// Enable/disable the joint spring. +B2_API void b2PrismaticJoint_EnableSpring( b2JointId jointId, bool enableSpring ); + +/// Is the prismatic joint spring enabled or not? +B2_API bool b2PrismaticJoint_IsSpringEnabled( b2JointId jointId ); + +/// Set the prismatic joint stiffness in Hertz. +/// This should usually be less than a quarter of the simulation rate. For example, if the simulation +/// runs at 60Hz then the joint stiffness should be 15Hz or less. +B2_API void b2PrismaticJoint_SetSpringHertz( b2JointId jointId, float hertz ); + +/// Get the prismatic joint stiffness in Hertz +B2_API float b2PrismaticJoint_GetSpringHertz( b2JointId jointId ); + +/// Set the prismatic joint damping ratio (non-dimensional) +B2_API void b2PrismaticJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the prismatic spring damping ratio (non-dimensional) +B2_API float b2PrismaticJoint_GetSpringDampingRatio( b2JointId jointId ); + +/// Set the prismatic joint spring target angle, usually in meters +B2_API void b2PrismaticJoint_SetTargetTranslation( b2JointId jointId, float translation ); + +/// Get the prismatic joint spring target translation, usually in meters +B2_API float b2PrismaticJoint_GetTargetTranslation( b2JointId jointId ); + +/// Enable/disable a prismatic joint limit +B2_API void b2PrismaticJoint_EnableLimit( b2JointId jointId, bool enableLimit ); + +/// Is the prismatic joint limit enabled? +B2_API bool b2PrismaticJoint_IsLimitEnabled( b2JointId jointId ); + +/// Get the prismatic joint lower limit +B2_API float b2PrismaticJoint_GetLowerLimit( b2JointId jointId ); + +/// Get the prismatic joint upper limit +B2_API float b2PrismaticJoint_GetUpperLimit( b2JointId jointId ); + +/// Set the prismatic joint limits +B2_API void b2PrismaticJoint_SetLimits( b2JointId jointId, float lower, float upper ); + +/// Enable/disable a prismatic joint motor +B2_API void b2PrismaticJoint_EnableMotor( b2JointId jointId, bool enableMotor ); + +/// Is the prismatic joint motor enabled? +B2_API bool b2PrismaticJoint_IsMotorEnabled( b2JointId jointId ); + +/// Set the prismatic joint motor speed, usually in meters per second +B2_API void b2PrismaticJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); + +/// Get the prismatic joint motor speed, usually in meters per second +B2_API float b2PrismaticJoint_GetMotorSpeed( b2JointId jointId ); + +/// Set the prismatic joint maximum motor force, usually in newtons +B2_API void b2PrismaticJoint_SetMaxMotorForce( b2JointId jointId, float force ); + +/// Get the prismatic joint maximum motor force, usually in newtons +B2_API float b2PrismaticJoint_GetMaxMotorForce( b2JointId jointId ); + +/// Get the prismatic joint current motor force, usually in newtons +B2_API float b2PrismaticJoint_GetMotorForce( b2JointId jointId ); + +/// Get the current joint translation, usually in meters. +B2_API float b2PrismaticJoint_GetTranslation( b2JointId jointId ); + +/// Get the current joint translation speed, usually in meters per second. +B2_API float b2PrismaticJoint_GetSpeed( b2JointId jointId ); + +/** @} */ + +/** + * @defgroup revolute_joint Revolute Joint + * @brief A revolute joint allows for relative rotation in the 2D plane with no relative translation. + * + * The revolute joint is probably the most common joint. It can be used for ragdolls and chains. + * Also called a *hinge* or *pin* joint. + * @{ + */ + +/// Create a revolute joint +/// @see b2RevoluteJointDef for details +B2_API b2JointId b2CreateRevoluteJoint( b2WorldId worldId, const b2RevoluteJointDef* def ); + +/// Enable/disable the revolute joint spring +B2_API void b2RevoluteJoint_EnableSpring( b2JointId jointId, bool enableSpring ); + +/// It the revolute angular spring enabled? +B2_API bool b2RevoluteJoint_IsSpringEnabled( b2JointId jointId ); + +/// Set the revolute joint spring stiffness in Hertz +B2_API void b2RevoluteJoint_SetSpringHertz( b2JointId jointId, float hertz ); + +/// Get the revolute joint spring stiffness in Hertz +B2_API float b2RevoluteJoint_GetSpringHertz( b2JointId jointId ); + +/// Set the revolute joint spring damping ratio, non-dimensional +B2_API void b2RevoluteJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the revolute joint spring damping ratio, non-dimensional +B2_API float b2RevoluteJoint_GetSpringDampingRatio( b2JointId jointId ); + +/// Set the revolute joint spring target angle, radians +B2_API void b2RevoluteJoint_SetTargetAngle( b2JointId jointId, float angle ); + +/// Get the revolute joint spring target angle, radians +B2_API float b2RevoluteJoint_GetTargetAngle( b2JointId jointId ); + +/// Get the revolute joint current angle in radians relative to the reference angle +/// @see b2RevoluteJointDef::referenceAngle +B2_API float b2RevoluteJoint_GetAngle( b2JointId jointId ); + +/// Enable/disable the revolute joint limit +B2_API void b2RevoluteJoint_EnableLimit( b2JointId jointId, bool enableLimit ); + +/// Is the revolute joint limit enabled? +B2_API bool b2RevoluteJoint_IsLimitEnabled( b2JointId jointId ); + +/// Get the revolute joint lower limit in radians +B2_API float b2RevoluteJoint_GetLowerLimit( b2JointId jointId ); + +/// Get the revolute joint upper limit in radians +B2_API float b2RevoluteJoint_GetUpperLimit( b2JointId jointId ); + +/// Set the revolute joint limits in radians. It is expected that lower <= upper +/// and that -0.99 * B2_PI <= lower && upper <= -0.99 * B2_PI. +B2_API void b2RevoluteJoint_SetLimits( b2JointId jointId, float lower, float upper ); + +/// Enable/disable a revolute joint motor +B2_API void b2RevoluteJoint_EnableMotor( b2JointId jointId, bool enableMotor ); + +/// Is the revolute joint motor enabled? +B2_API bool b2RevoluteJoint_IsMotorEnabled( b2JointId jointId ); + +/// Set the revolute joint motor speed in radians per second +B2_API void b2RevoluteJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); + +/// Get the revolute joint motor speed in radians per second +B2_API float b2RevoluteJoint_GetMotorSpeed( b2JointId jointId ); + +/// Get the revolute joint current motor torque, usually in newton-meters +B2_API float b2RevoluteJoint_GetMotorTorque( b2JointId jointId ); + +/// Set the revolute joint maximum motor torque, usually in newton-meters +B2_API void b2RevoluteJoint_SetMaxMotorTorque( b2JointId jointId, float torque ); + +/// Get the revolute joint maximum motor torque, usually in newton-meters +B2_API float b2RevoluteJoint_GetMaxMotorTorque( b2JointId jointId ); + +/**@}*/ + +/** + * @defgroup weld_joint Weld Joint + * @brief A weld joint fully constrains the relative transform between two bodies while allowing for springiness + * + * A weld joint constrains the relative rotation and translation between two bodies. Both rotation and translation + * can have damped springs. + * + * @note The accuracy of weld joint is limited by the accuracy of the solver. Long chains of weld joints may flex. + * @{ + */ + +/// Create a weld joint +/// @see b2WeldJointDef for details +B2_API b2JointId b2CreateWeldJoint( b2WorldId worldId, const b2WeldJointDef* def ); + +/// Set the weld joint linear stiffness in Hertz. 0 is rigid. +B2_API void b2WeldJoint_SetLinearHertz( b2JointId jointId, float hertz ); + +/// Get the weld joint linear stiffness in Hertz +B2_API float b2WeldJoint_GetLinearHertz( b2JointId jointId ); + +/// Set the weld joint linear damping ratio (non-dimensional) +B2_API void b2WeldJoint_SetLinearDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the weld joint linear damping ratio (non-dimensional) +B2_API float b2WeldJoint_GetLinearDampingRatio( b2JointId jointId ); + +/// Set the weld joint angular stiffness in Hertz. 0 is rigid. +B2_API void b2WeldJoint_SetAngularHertz( b2JointId jointId, float hertz ); + +/// Get the weld joint angular stiffness in Hertz +B2_API float b2WeldJoint_GetAngularHertz( b2JointId jointId ); + +/// Set weld joint angular damping ratio, non-dimensional +B2_API void b2WeldJoint_SetAngularDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the weld joint angular damping ratio, non-dimensional +B2_API float b2WeldJoint_GetAngularDampingRatio( b2JointId jointId ); + +/** @} */ + +/** + * @defgroup wheel_joint Wheel Joint + * The wheel joint can be used to simulate wheels on vehicles. + * + * The wheel joint restricts body B to move along a local axis in body A. Body B is free to + * rotate. Supports a linear spring, linear limits, and a rotational motor. + * + * @{ + */ + +/// Create a wheel joint +/// @see b2WheelJointDef for details +B2_API b2JointId b2CreateWheelJoint( b2WorldId worldId, const b2WheelJointDef* def ); + +/// Enable/disable the wheel joint spring +B2_API void b2WheelJoint_EnableSpring( b2JointId jointId, bool enableSpring ); + +/// Is the wheel joint spring enabled? +B2_API bool b2WheelJoint_IsSpringEnabled( b2JointId jointId ); + +/// Set the wheel joint stiffness in Hertz +B2_API void b2WheelJoint_SetSpringHertz( b2JointId jointId, float hertz ); + +/// Get the wheel joint stiffness in Hertz +B2_API float b2WheelJoint_GetSpringHertz( b2JointId jointId ); + +/// Set the wheel joint damping ratio, non-dimensional +B2_API void b2WheelJoint_SetSpringDampingRatio( b2JointId jointId, float dampingRatio ); + +/// Get the wheel joint damping ratio, non-dimensional +B2_API float b2WheelJoint_GetSpringDampingRatio( b2JointId jointId ); + +/// Enable/disable the wheel joint limit +B2_API void b2WheelJoint_EnableLimit( b2JointId jointId, bool enableLimit ); + +/// Is the wheel joint limit enabled? +B2_API bool b2WheelJoint_IsLimitEnabled( b2JointId jointId ); + +/// Get the wheel joint lower limit +B2_API float b2WheelJoint_GetLowerLimit( b2JointId jointId ); + +/// Get the wheel joint upper limit +B2_API float b2WheelJoint_GetUpperLimit( b2JointId jointId ); + +/// Set the wheel joint limits +B2_API void b2WheelJoint_SetLimits( b2JointId jointId, float lower, float upper ); + +/// Enable/disable the wheel joint motor +B2_API void b2WheelJoint_EnableMotor( b2JointId jointId, bool enableMotor ); + +/// Is the wheel joint motor enabled? +B2_API bool b2WheelJoint_IsMotorEnabled( b2JointId jointId ); + +/// Set the wheel joint motor speed in radians per second +B2_API void b2WheelJoint_SetMotorSpeed( b2JointId jointId, float motorSpeed ); + +/// Get the wheel joint motor speed in radians per second +B2_API float b2WheelJoint_GetMotorSpeed( b2JointId jointId ); + +/// Set the wheel joint maximum motor torque, usually in newton-meters +B2_API void b2WheelJoint_SetMaxMotorTorque( b2JointId jointId, float torque ); + +/// Get the wheel joint maximum motor torque, usually in newton-meters +B2_API float b2WheelJoint_GetMaxMotorTorque( b2JointId jointId ); + +/// Get the wheel joint current motor torque, usually in newton-meters +B2_API float b2WheelJoint_GetMotorTorque( b2JointId jointId ); + +/**@}*/ + +/**@}*/ + +/** + * @defgroup contact Contact + * Access to contacts + * @{ + */ + +/// Contact identifier validation. Provides validation for up to 2^32 allocations. +B2_API bool b2Contact_IsValid( b2ContactId id ); + +/// Get the data for a contact. The manifold may have no points if the contact is not touching. +B2_API b2ContactData b2Contact_GetData( b2ContactId contactId ); + +/**@}*/ diff --git a/libs/box2d/include/box2d/collision.h b/libs/box2d/include/box2d/collision.h new file mode 100644 index 0000000..003b1d1 --- /dev/null +++ b/libs/box2d/include/box2d/collision.h @@ -0,0 +1,851 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" +#include "math_functions.h" + +#include + +typedef struct b2SimplexCache b2SimplexCache; +typedef struct b2Hull b2Hull; + +/** + * @defgroup geometry Geometry + * @brief Geometry types and algorithms + * + * Definitions of circles, capsules, segments, and polygons. Various algorithms to compute hulls, mass properties, and so on. + * Functions should take the shape as the first argument to assist editor auto-complete. + * @{ + */ + +/// The maximum number of vertices on a convex polygon. Changing this affects performance even if you +/// don't use more vertices. +#define B2_MAX_POLYGON_VERTICES 8 + +/// Low level ray cast input data +typedef struct b2RayCastInput +{ + /// Start point of the ray cast + b2Vec2 origin; + + /// Translation of the ray cast + b2Vec2 translation; + + /// The maximum fraction of the translation to consider, typically 1 + float maxFraction; +} b2RayCastInput; + +/// A distance proxy is used by the GJK algorithm. It encapsulates any shape. +/// You can provide between 1 and B2_MAX_POLYGON_VERTICES and a radius. +typedef struct b2ShapeProxy +{ + /// The point cloud + b2Vec2 points[B2_MAX_POLYGON_VERTICES]; + + /// The number of points. Must be greater than 0. + int count; + + /// The external radius of the point cloud. May be zero. + float radius; +} b2ShapeProxy; + +/// Low level shape cast input in generic form. This allows casting an arbitrary point +/// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius. +/// A capsule is two points with a non-zero radius. A box is four points with a zero radius. +typedef struct b2ShapeCastInput +{ + /// A generic shape + b2ShapeProxy proxy; + + /// The translation of the shape cast + b2Vec2 translation; + + /// The maximum fraction of the translation to consider, typically 1 + float maxFraction; + + /// Allow shape cast to encroach when initially touching. This only works if the radius is greater than zero. + bool canEncroach; +} b2ShapeCastInput; + +/// Low level ray cast or shape-cast output data. Returns a zero fraction and normal in the case of initial overlap. +typedef struct b2CastOutput +{ + /// The surface normal at the hit point + b2Vec2 normal; + + /// The surface hit point + b2Vec2 point; + + /// The fraction of the input translation at collision + float fraction; + + /// The number of iterations used + int iterations; + + /// Did the cast hit? + bool hit; +} b2CastOutput; + +/// This holds the mass data computed for a shape. +typedef struct b2MassData +{ + /// The mass of the shape, usually in kilograms. + float mass; + + /// The position of the shape's centroid relative to the shape's origin. + b2Vec2 center; + + /// The rotational inertia of the shape about the shape center. + float rotationalInertia; +} b2MassData; + +/// A solid circle +typedef struct b2Circle +{ + /// The local center + b2Vec2 center; + + /// The radius + float radius; +} b2Circle; + +/// A solid capsule can be viewed as two semicircles connected +/// by a rectangle. +typedef struct b2Capsule +{ + /// Local center of the first semicircle + b2Vec2 center1; + + /// Local center of the second semicircle + b2Vec2 center2; + + /// The radius of the semicircles + float radius; +} b2Capsule; + +/// A solid convex polygon. It is assumed that the interior of the polygon is to +/// the left of each edge. +/// Polygons have a maximum number of vertices equal to B2_MAX_POLYGON_VERTICES. +/// In most cases you should not need many vertices for a convex polygon. +/// @warning DO NOT fill this out manually, instead use a helper function like +/// b2MakePolygon or b2MakeBox. +typedef struct b2Polygon +{ + /// The polygon vertices + b2Vec2 vertices[B2_MAX_POLYGON_VERTICES]; + + /// The outward normal vectors of the polygon sides + b2Vec2 normals[B2_MAX_POLYGON_VERTICES]; + + /// The centroid of the polygon + b2Vec2 centroid; + + /// The external radius for rounded polygons + float radius; + + /// The number of polygon vertices + int count; +} b2Polygon; + +/// A line segment with two-sided collision. +typedef struct b2Segment +{ + /// The first point + b2Vec2 point1; + + /// The second point + b2Vec2 point2; +} b2Segment; + +/// A line segment with one-sided collision. Only collides on the right side. +/// Several of these are generated for a chain shape. +/// ghost1 -> point1 -> point2 -> ghost2 +typedef struct b2ChainSegment +{ + /// The tail ghost vertex + b2Vec2 ghost1; + + /// The line segment + b2Segment segment; + + /// The head ghost vertex + b2Vec2 ghost2; + + /// The owning chain shape index (internal usage only) + int chainId; +} b2ChainSegment; + +/// Validate ray cast input data (NaN, etc) +B2_API bool b2IsValidRay( const b2RayCastInput* input ); + +/// Make a convex polygon from a convex hull. This will assert if the hull is not valid. +/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull +B2_API b2Polygon b2MakePolygon( const b2Hull* hull, float radius ); + +/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid. +/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull +B2_API b2Polygon b2MakeOffsetPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation ); + +/// Make an offset convex polygon from a convex hull. This will assert if the hull is not valid. +/// @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull +B2_API b2Polygon b2MakeOffsetRoundedPolygon( const b2Hull* hull, b2Vec2 position, b2Rot rotation, float radius ); + +/// Make a square polygon, bypassing the need for a convex hull. +/// @param halfWidth the half-width +B2_API b2Polygon b2MakeSquare( float halfWidth ); + +/// Make a box (rectangle) polygon, bypassing the need for a convex hull. +/// @param halfWidth the half-width (x-axis) +/// @param halfHeight the half-height (y-axis) +B2_API b2Polygon b2MakeBox( float halfWidth, float halfHeight ); + +/// Make a rounded box, bypassing the need for a convex hull. +/// @param halfWidth the half-width (x-axis) +/// @param halfHeight the half-height (y-axis) +/// @param radius the radius of the rounded extension +B2_API b2Polygon b2MakeRoundedBox( float halfWidth, float halfHeight, float radius ); + +/// Make an offset box, bypassing the need for a convex hull. +/// @param halfWidth the half-width (x-axis) +/// @param halfHeight the half-height (y-axis) +/// @param center the local center of the box +/// @param rotation the local rotation of the box +B2_API b2Polygon b2MakeOffsetBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation ); + +/// Make an offset rounded box, bypassing the need for a convex hull. +/// @param halfWidth the half-width (x-axis) +/// @param halfHeight the half-height (y-axis) +/// @param center the local center of the box +/// @param rotation the local rotation of the box +/// @param radius the radius of the rounded extension +B2_API b2Polygon b2MakeOffsetRoundedBox( float halfWidth, float halfHeight, b2Vec2 center, b2Rot rotation, float radius ); + +/// Transform a polygon. This is useful for transferring a shape from one body to another. +B2_API b2Polygon b2TransformPolygon( b2Transform transform, const b2Polygon* polygon ); + +/// Compute mass properties of a circle +B2_API b2MassData b2ComputeCircleMass( const b2Circle* shape, float density ); + +/// Compute mass properties of a capsule +B2_API b2MassData b2ComputeCapsuleMass( const b2Capsule* shape, float density ); + +/// Compute mass properties of a polygon +B2_API b2MassData b2ComputePolygonMass( const b2Polygon* shape, float density ); + +/// Compute the bounding box of a transformed circle +B2_API b2AABB b2ComputeCircleAABB( const b2Circle* shape, b2Transform transform ); + +/// Compute the bounding box of a transformed capsule +B2_API b2AABB b2ComputeCapsuleAABB( const b2Capsule* shape, b2Transform transform ); + +/// Compute the bounding box of a transformed polygon +B2_API b2AABB b2ComputePolygonAABB( const b2Polygon* shape, b2Transform transform ); + +/// Compute the bounding box of a transformed line segment +B2_API b2AABB b2ComputeSegmentAABB( const b2Segment* shape, b2Transform transform ); + +/// Test a point for overlap with a circle in local space +B2_API bool b2PointInCircle( const b2Circle* shape, b2Vec2 point ); + +/// Test a point for overlap with a capsule in local space +B2_API bool b2PointInCapsule( const b2Capsule* shape, b2Vec2 point ); + +/// Test a point for overlap with a convex polygon in local space +B2_API bool b2PointInPolygon( const b2Polygon* shape, b2Vec2 point ); + +/// Ray cast versus circle shape in local space. +B2_API b2CastOutput b2RayCastCircle( const b2Circle* shape, const b2RayCastInput* input ); + +/// Ray cast versus capsule shape in local space. +B2_API b2CastOutput b2RayCastCapsule( const b2Capsule* shape, const b2RayCastInput* input ); + +/// Ray cast versus segment shape in local space. Optionally treat the segment as one-sided with hits from +/// the left side being treated as a miss. +B2_API b2CastOutput b2RayCastSegment( const b2Segment* shape, const b2RayCastInput* input, bool oneSided ); + +/// Ray cast versus polygon shape in local space. +B2_API b2CastOutput b2RayCastPolygon( const b2Polygon* shape, const b2RayCastInput* input ); + +/// Shape cast versus a circle. +B2_API b2CastOutput b2ShapeCastCircle(const b2Circle* shape, const b2ShapeCastInput* input ); + +/// Shape cast versus a capsule. +B2_API b2CastOutput b2ShapeCastCapsule( const b2Capsule* shape, const b2ShapeCastInput* input); + +/// Shape cast versus a line segment. +B2_API b2CastOutput b2ShapeCastSegment( const b2Segment* shape, const b2ShapeCastInput* input ); + +/// Shape cast versus a convex polygon. +B2_API b2CastOutput b2ShapeCastPolygon( const b2Polygon* shape, const b2ShapeCastInput* input ); + +/// A convex hull. Used to create convex polygons. +/// @warning Do not modify these values directly, instead use b2ComputeHull() +typedef struct b2Hull +{ + /// The final points of the hull + b2Vec2 points[B2_MAX_POLYGON_VERTICES]; + + /// The number of points + int count; +} b2Hull; + +/// Compute the convex hull of a set of points. Returns an empty hull if it fails. +/// Some failure cases: +/// - all points very close together +/// - all points on a line +/// - less than 3 points +/// - more than B2_MAX_POLYGON_VERTICES points +/// This welds close points and removes collinear points. +/// @warning Do not modify a hull once it has been computed +B2_API b2Hull b2ComputeHull( const b2Vec2* points, int count ); + +/// This determines if a hull is valid. Checks for: +/// - convexity +/// - collinear points +/// This is expensive and should not be called at runtime. +B2_API bool b2ValidateHull( const b2Hull* hull ); + +/**@}*/ + +/** + * @defgroup distance Distance + * Functions for computing the distance between shapes. + * + * These are advanced functions you can use to perform distance calculations. There + * are functions for computing the closest points between shapes, doing linear shape casts, + * and doing rotational shape casts. The latter is called time of impact (TOI). + * @{ + */ + +/// Result of computing the distance between two line segments +typedef struct b2SegmentDistanceResult +{ + /// The closest point on the first segment + b2Vec2 closest1; + + /// The closest point on the second segment + b2Vec2 closest2; + + /// The barycentric coordinate on the first segment + float fraction1; + + /// The barycentric coordinate on the second segment + float fraction2; + + /// The squared distance between the closest points + float distanceSquared; +} b2SegmentDistanceResult; + +/// Compute the distance between two line segments, clamping at the end points if needed. +B2_API b2SegmentDistanceResult b2SegmentDistance( b2Vec2 p1, b2Vec2 q1, b2Vec2 p2, b2Vec2 q2 ); + +/// Used to warm start the GJK simplex. If you call this function multiple times with nearby +/// transforms this might improve performance. Otherwise you can zero initialize this. +/// The distance cache must be initialized to zero on the first call. +/// Users should generally just zero initialize this structure for each call. +typedef struct b2SimplexCache +{ + /// The number of stored simplex points + uint16_t count; + + /// The cached simplex indices on shape A + uint8_t indexA[3]; + + /// The cached simplex indices on shape B + uint8_t indexB[3]; +} b2SimplexCache; + +static const b2SimplexCache b2_emptySimplexCache = B2_ZERO_INIT; + +/// Input for b2ShapeDistance +typedef struct b2DistanceInput +{ + /// The proxy for shape A + b2ShapeProxy proxyA; + + /// The proxy for shape B + b2ShapeProxy proxyB; + + /// The world transform for shape A + b2Transform transformA; + + /// The world transform for shape B + b2Transform transformB; + + /// Should the proxy radius be considered? + bool useRadii; +} b2DistanceInput; + +/// Output for b2ShapeDistance +typedef struct b2DistanceOutput +{ + b2Vec2 pointA; ///< Closest point on shapeA + b2Vec2 pointB; ///< Closest point on shapeB + b2Vec2 normal; ///< Normal vector that points from A to B. Invalid if distance is zero. + float distance; ///< The final distance, zero if overlapped + int iterations; ///< Number of GJK iterations used + int simplexCount; ///< The number of simplexes stored in the simplex array +} b2DistanceOutput; + +/// Simplex vertex for debugging the GJK algorithm +typedef struct b2SimplexVertex +{ + b2Vec2 wA; ///< support point in proxyA + b2Vec2 wB; ///< support point in proxyB + b2Vec2 w; ///< wB - wA + float a; ///< barycentric coordinate for closest point + int indexA; ///< wA index + int indexB; ///< wB index +} b2SimplexVertex; + +/// Simplex from the GJK algorithm +typedef struct b2Simplex +{ + b2SimplexVertex v1, v2, v3; ///< vertices + int count; ///< number of valid vertices +} b2Simplex; + +/// Compute the closest points between two shapes represented as point clouds. +/// b2SimplexCache cache is input/output. On the first call set b2SimplexCache.count to zero. +/// The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these. +B2_API b2DistanceOutput b2ShapeDistance( const b2DistanceInput* input, b2SimplexCache* cache, b2Simplex* simplexes, + int simplexCapacity ); + +/// Input parameters for b2ShapeCast +typedef struct b2ShapeCastPairInput +{ + b2ShapeProxy proxyA; ///< The proxy for shape A + b2ShapeProxy proxyB; ///< The proxy for shape B + b2Transform transformA; ///< The world transform for shape A + b2Transform transformB; ///< The world transform for shape B + b2Vec2 translationB; ///< The translation of shape B + float maxFraction; ///< The fraction of the translation to consider, typically 1 + bool canEncroach; ///< Allows shapes with a radius to move slightly closer if already touching +} b2ShapeCastPairInput; + +/// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction. +/// Initially touching shapes are treated as a miss. +B2_API b2CastOutput b2ShapeCast( const b2ShapeCastPairInput* input ); + +/// Make a proxy for use in overlap, shape cast, and related functions. This is a deep copy of the points. +B2_API b2ShapeProxy b2MakeProxy( const b2Vec2* points, int count, float radius ); + +/// Make a proxy with a transform. This is a deep copy of the points. +B2_API b2ShapeProxy b2MakeOffsetProxy( const b2Vec2* points, int count, float radius, b2Vec2 position, b2Rot rotation ); + +/// This describes the motion of a body/shape for TOI computation. Shapes are defined with respect to the body origin, +/// which may not coincide with the center of mass. However, to support dynamics we must interpolate the center of mass +/// position. +typedef struct b2Sweep +{ + b2Vec2 localCenter; ///< Local center of mass position + b2Vec2 c1; ///< Starting center of mass world position + b2Vec2 c2; ///< Ending center of mass world position + b2Rot q1; ///< Starting world rotation + b2Rot q2; ///< Ending world rotation +} b2Sweep; + +/// Evaluate the transform sweep at a specific time. +B2_API b2Transform b2GetSweepTransform( const b2Sweep* sweep, float time ); + +/// Time of impact input +typedef struct b2TOIInput +{ + b2ShapeProxy proxyA; ///< The proxy for shape A + b2ShapeProxy proxyB; ///< The proxy for shape B + b2Sweep sweepA; ///< The movement of shape A + b2Sweep sweepB; ///< The movement of shape B + float maxFraction; ///< Defines the sweep interval [0, maxFraction] +} b2TOIInput; + +/// Describes the TOI output +typedef enum b2TOIState +{ + b2_toiStateUnknown, + b2_toiStateFailed, + b2_toiStateOverlapped, + b2_toiStateHit, + b2_toiStateSeparated +} b2TOIState; + +/// Time of impact output +typedef struct b2TOIOutput +{ + /// The type of result + b2TOIState state; + + /// The hit point + b2Vec2 point; + + /// The hit normal + b2Vec2 normal; + + /// The sweep time of the collision + float fraction; +} b2TOIOutput; + +/// Compute the upper bound on time before two shapes penetrate. Time is represented as +/// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate, +/// non-tunneling collisions. If you change the time interval, you should call this function +/// again. +B2_API b2TOIOutput b2TimeOfImpact( const b2TOIInput* input ); + +/**@}*/ + +/** + * @defgroup collision Collision + * @brief Functions for colliding pairs of shapes + * @{ + */ + +/// A manifold point is a contact point belonging to a contact manifold. +/// It holds details related to the geometry and dynamics of the contact points. +/// Box2D uses speculative collision so some contact points may be separated. +/// You may use the totalNormalImpulse to determine if there was an interaction during +/// the time step. +typedef struct b2ManifoldPoint +{ + /// Location of the contact point in world space. Subject to precision loss at large coordinates. + /// @note Should only be used for debugging. + b2Vec2 point; + + /// Location of the contact point relative to shapeA's origin in world space + /// @note When used internally to the Box2D solver, this is relative to the body center of mass. + b2Vec2 anchorA; + + /// Location of the contact point relative to shapeB's origin in world space + /// @note When used internally to the Box2D solver, this is relative to the body center of mass. + b2Vec2 anchorB; + + /// The separation of the contact point, negative if penetrating + float separation; + + /// The impulse along the manifold normal vector. + float normalImpulse; + + /// The friction impulse + float tangentImpulse; + + /// The total normal impulse applied across sub-stepping and restitution. This is important + /// to identify speculative contact points that had an interaction in the time step. + /// This includes the warm starting impulse, the sub-step delta impulse, and the restitution + /// impulse. + float totalNormalImpulse; + + /// Relative normal velocity pre-solve. Used for hit events. If the normal impulse is + /// zero then there was no hit. Negative means shapes are approaching. + float normalVelocity; + + /// Uniquely identifies a contact point between two shapes + uint16_t id; + + /// Did this contact point exist the previous step? + bool persisted; +} b2ManifoldPoint; + +/// A contact manifold describes the contact points between colliding shapes. +/// @note Box2D uses speculative collision so some contact points may be separated. +typedef struct b2Manifold +{ + /// The unit normal vector in world space, points from shape A to bodyB + b2Vec2 normal; + + /// Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s + float rollingImpulse; + + /// The manifold points, up to two are possible in 2D + b2ManifoldPoint points[2]; + + /// The number of contacts points, will be 0, 1, or 2 + int pointCount; + +} b2Manifold; + +/// Compute the contact manifold between two circles +B2_API b2Manifold b2CollideCircles( const b2Circle* circleA, b2Transform xfA, const b2Circle* circleB, b2Transform xfB ); + +/// Compute the contact manifold between a capsule and circle +B2_API b2Manifold b2CollideCapsuleAndCircle( const b2Capsule* capsuleA, b2Transform xfA, const b2Circle* circleB, + b2Transform xfB ); + +/// Compute the contact manifold between an segment and a circle +B2_API b2Manifold b2CollideSegmentAndCircle( const b2Segment* segmentA, b2Transform xfA, const b2Circle* circleB, + b2Transform xfB ); + +/// Compute the contact manifold between a polygon and a circle +B2_API b2Manifold b2CollidePolygonAndCircle( const b2Polygon* polygonA, b2Transform xfA, const b2Circle* circleB, + b2Transform xfB ); + +/// Compute the contact manifold between a capsule and circle +B2_API b2Manifold b2CollideCapsules( const b2Capsule* capsuleA, b2Transform xfA, const b2Capsule* capsuleB, b2Transform xfB ); + +/// Compute the contact manifold between an segment and a capsule +B2_API b2Manifold b2CollideSegmentAndCapsule( const b2Segment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, + b2Transform xfB ); + +/// Compute the contact manifold between a polygon and capsule +B2_API b2Manifold b2CollidePolygonAndCapsule( const b2Polygon* polygonA, b2Transform xfA, const b2Capsule* capsuleB, + b2Transform xfB ); + +/// Compute the contact manifold between two polygons +B2_API b2Manifold b2CollidePolygons( const b2Polygon* polygonA, b2Transform xfA, const b2Polygon* polygonB, b2Transform xfB ); + +/// Compute the contact manifold between an segment and a polygon +B2_API b2Manifold b2CollideSegmentAndPolygon( const b2Segment* segmentA, b2Transform xfA, const b2Polygon* polygonB, + b2Transform xfB ); + +/// Compute the contact manifold between a chain segment and a circle +B2_API b2Manifold b2CollideChainSegmentAndCircle( const b2ChainSegment* segmentA, b2Transform xfA, const b2Circle* circleB, + b2Transform xfB ); + +/// Compute the contact manifold between a chain segment and a capsule +B2_API b2Manifold b2CollideChainSegmentAndCapsule( const b2ChainSegment* segmentA, b2Transform xfA, const b2Capsule* capsuleB, + b2Transform xfB, b2SimplexCache* cache ); + +/// Compute the contact manifold between a chain segment and a rounded polygon +B2_API b2Manifold b2CollideChainSegmentAndPolygon( const b2ChainSegment* segmentA, b2Transform xfA, const b2Polygon* polygonB, + b2Transform xfB, b2SimplexCache* cache ); + +/**@}*/ + +/** + * @defgroup tree Dynamic Tree + * The dynamic tree is a binary AABB tree to organize and query large numbers of geometric objects + * + * Box2D uses the dynamic tree internally to sort collision shapes into a binary bounding volume hierarchy. + * This data structure may have uses in games for organizing other geometry data and may be used independently + * of Box2D rigid body simulation. + * + * A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt. + * A dynamic tree arranges data in a binary tree to accelerate + * queries such as AABB queries and ray casts. Leaf nodes are proxies + * with an AABB. These are used to hold a user collision object. + * Nodes are pooled and relocatable, so I use node indices rather than pointers. + * The dynamic tree is made available for advanced users that would like to use it to organize + * spatial game data besides rigid bodies. + * @{ + */ + +/// The dynamic tree structure. This should be considered private data. +/// It is placed here for performance reasons. +typedef struct b2DynamicTree +{ + /// The tree nodes + struct b2TreeNode* nodes; + + /// The root index + int root; + + /// The number of nodes + int nodeCount; + + /// The allocated node space + int nodeCapacity; + + /// Node free list + int freeList; + + /// Number of proxies created + int proxyCount; + + /// Leaf indices for rebuild + int* leafIndices; + + /// Leaf bounding boxes for rebuild + b2AABB* leafBoxes; + + /// Leaf bounding box centers for rebuild + b2Vec2* leafCenters; + + /// Bins for sorting during rebuild + int* binIndices; + + /// Allocated space for rebuilding + int rebuildCapacity; +} b2DynamicTree; + +/// These are performance results returned by dynamic tree queries. +typedef struct b2TreeStats +{ + /// Number of internal nodes visited during the query + int nodeVisits; + + /// Number of leaf nodes visited during the query + int leafVisits; +} b2TreeStats; + +/// Constructing the tree initializes the node pool. +B2_API b2DynamicTree b2DynamicTree_Create( void ); + +/// Destroy the tree, freeing the node pool. +B2_API void b2DynamicTree_Destroy( b2DynamicTree* tree ); + +/// Create a proxy. Provide an AABB and a userData value. +B2_API int b2DynamicTree_CreateProxy( b2DynamicTree* tree, b2AABB aabb, uint64_t categoryBits, uint64_t userData ); + +/// Destroy a proxy. This asserts if the id is invalid. +B2_API void b2DynamicTree_DestroyProxy( b2DynamicTree* tree, int proxyId ); + +/// Move a proxy to a new AABB by removing and reinserting into the tree. +B2_API void b2DynamicTree_MoveProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb ); + +/// Enlarge a proxy and enlarge ancestors as necessary. +B2_API void b2DynamicTree_EnlargeProxy( b2DynamicTree* tree, int proxyId, b2AABB aabb ); + +/// Modify the category bits on a proxy. This is an expensive operation. +B2_API void b2DynamicTree_SetCategoryBits( b2DynamicTree* tree, int proxyId, uint64_t categoryBits ); + +/// Get the category bits on a proxy. +B2_API uint64_t b2DynamicTree_GetCategoryBits( b2DynamicTree* tree, int proxyId ); + +/// This function receives proxies found in the AABB query. +/// @return true if the query should continue +typedef bool b2TreeQueryCallbackFcn( int proxyId, uint64_t userData, void* context ); + +/// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB. +/// @return performance data +B2_API b2TreeStats b2DynamicTree_Query( const b2DynamicTree* tree, b2AABB aabb, uint64_t maskBits, + b2TreeQueryCallbackFcn* callback, void* context ); + +/// Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB. +/// No filtering is performed. +/// @return performance data +B2_API b2TreeStats b2DynamicTree_QueryAll( const b2DynamicTree* tree, b2AABB aabb, b2TreeQueryCallbackFcn* callback, + void* context ); + +/// This function receives clipped ray cast input for a proxy. The function +/// returns the new ray fraction. +/// - return a value of 0 to terminate the ray cast +/// - return a value less than input->maxFraction to clip the ray +/// - return a value of input->maxFraction to continue the ray cast without clipping +typedef float b2TreeRayCastCallbackFcn( const b2RayCastInput* input, int proxyId, uint64_t userData, void* context ); + +/// Ray cast against the proxies in the tree. This relies on the callback +/// to perform a exact ray cast in the case were the proxy contains a shape. +/// The callback also performs the any collision filtering. This has performance +/// roughly equal to k * log(n), where k is the number of collisions and n is the +/// number of proxies in the tree. +/// Bit-wise filtering using mask bits can greatly improve performance in some scenarios. +/// However, this filtering may be approximate, so the user should still apply filtering to results. +/// @param tree the dynamic tree to ray cast +/// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1) +/// @param maskBits mask bit hint: `bool accept = (maskBits & node->categoryBits) != 0;` +/// @param callback a callback class that is called for each proxy that is hit by the ray +/// @param context user context that is passed to the callback +/// @return performance data +B2_API b2TreeStats b2DynamicTree_RayCast( const b2DynamicTree* tree, const b2RayCastInput* input, uint64_t maskBits, + b2TreeRayCastCallbackFcn* callback, void* context ); + +/// This function receives clipped ray cast input for a proxy. The function +/// returns the new ray fraction. +/// - return a value of 0 to terminate the ray cast +/// - return a value less than input->maxFraction to clip the ray +/// - return a value of input->maxFraction to continue the ray cast without clipping +typedef float b2TreeShapeCastCallbackFcn( const b2ShapeCastInput* input, int proxyId, uint64_t userData, void* context ); + +/// Ray cast against the proxies in the tree. This relies on the callback +/// to perform a exact ray cast in the case were the proxy contains a shape. +/// The callback also performs the any collision filtering. This has performance +/// roughly equal to k * log(n), where k is the number of collisions and n is the +/// number of proxies in the tree. +/// @param tree the dynamic tree to ray cast +/// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). +/// @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0;` +/// @param callback a callback class that is called for each proxy that is hit by the shape +/// @param context user context that is passed to the callback +/// @return performance data +B2_API b2TreeStats b2DynamicTree_ShapeCast( const b2DynamicTree* tree, const b2ShapeCastInput* input, uint64_t maskBits, + b2TreeShapeCastCallbackFcn* callback, void* context ); + +/// Get the height of the binary tree. +B2_API int b2DynamicTree_GetHeight( const b2DynamicTree* tree ); + +/// Get the ratio of the sum of the node areas to the root area. +B2_API float b2DynamicTree_GetAreaRatio( const b2DynamicTree* tree ); + +/// Get the bounding box that contains the entire tree +B2_API b2AABB b2DynamicTree_GetRootBounds( const b2DynamicTree* tree ); + +/// Get the number of proxies created +B2_API int b2DynamicTree_GetProxyCount( const b2DynamicTree* tree ); + +/// Rebuild the tree while retaining subtrees that haven't changed. Returns the number of boxes sorted. +B2_API int b2DynamicTree_Rebuild( b2DynamicTree* tree, bool fullBuild ); + +/// Get the number of bytes used by this tree +B2_API int b2DynamicTree_GetByteCount( const b2DynamicTree* tree ); + +/// Get proxy user data +B2_API uint64_t b2DynamicTree_GetUserData( const b2DynamicTree* tree, int proxyId ); + +/// Get the AABB of a proxy +B2_API b2AABB b2DynamicTree_GetAABB( const b2DynamicTree* tree, int proxyId ); + +/// Validate this tree. For testing. +B2_API void b2DynamicTree_Validate( const b2DynamicTree* tree ); + +/// Validate this tree has no enlarged AABBs. For testing. +B2_API void b2DynamicTree_ValidateNoEnlarged( const b2DynamicTree* tree ); + +/**@}*/ + +/** + * @defgroup character Character mover + * Character movement solver + * @{ + */ + +/// These are the collision planes returned from b2World_CollideMover +typedef struct b2PlaneResult +{ + /// The collision plane between the mover and a convex shape + b2Plane plane; + + // The collision point on the shape. + b2Vec2 point; + + /// Did the collision register a hit? If not this plane should be ignored. + bool hit; +} b2PlaneResult; + +/// These are collision planes that can be fed to b2SolvePlanes. Normally +/// this is assembled by the user from plane results in b2PlaneResult +typedef struct b2CollisionPlane +{ + /// The collision plane between the mover and some shape + b2Plane plane; + + /// Setting this to FLT_MAX makes the plane as rigid as possible. Lower values can + /// make the plane collision soft. Usually in meters. + float pushLimit; + + /// The push on the mover determined by b2SolvePlanes. Usually in meters. + float push; + + /// Indicates if b2ClipVector should clip against this plane. Should be false for soft collision. + bool clipVelocity; +} b2CollisionPlane; + +/// Result returned by b2SolvePlanes +typedef struct b2PlaneSolverResult +{ + /// The translation of the mover + b2Vec2 translation; + + /// The number of iterations used by the plane solver. For diagnostics. + int iterationCount; +} b2PlaneSolverResult; + +/// Solves the position of a mover that satisfies the given collision planes. +/// @param targetDelta the desired movement from the position used to generate the collision planes +/// @param planes the collision planes +/// @param count the number of collision planes +B2_API b2PlaneSolverResult b2SolvePlanes( b2Vec2 targetDelta, b2CollisionPlane* planes, int count ); + +/// Clips the velocity against the given collision planes. Planes with zero push or clipVelocity +/// set to false are skipped. +B2_API b2Vec2 b2ClipVector( b2Vec2 vector, const b2CollisionPlane* planes, int count ); + +/**@}*/ diff --git a/libs/box2d/include/box2d/id.h b/libs/box2d/include/box2d/id.h new file mode 100644 index 0000000..a698084 --- /dev/null +++ b/libs/box2d/include/box2d/id.h @@ -0,0 +1,194 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +// Note: this file should be stand-alone + +/** + * @defgroup id Ids + * These ids serve as handles to internal Box2D objects. + * These should be considered opaque data and passed by value. + * Include this header if you need the id types and not the whole Box2D API. + * All ids are considered null if initialized to zero. + * + * For example in C++: + * + * @code{.cxx} + * b2WorldId worldId = {}; + * @endcode + * + * Or in C: + * + * @code{.c} + * b2WorldId worldId = {0}; + * @endcode + * + * These are both considered null. + * + * @warning Do not use the internals of these ids. They are subject to change. Ids should be treated as opaque objects. + * @warning You should use ids to access objects in Box2D. Do not access files within the src folder. Such usage is unsupported. + * @{ + */ + +/// World id references a world instance. This should be treated as an opaque handle. +typedef struct b2WorldId +{ + uint16_t index1; + uint16_t generation; +} b2WorldId; + +/// Body id references a body instance. This should be treated as an opaque handle. +typedef struct b2BodyId +{ + int32_t index1; + uint16_t world0; + uint16_t generation; +} b2BodyId; + +/// Shape id references a shape instance. This should be treated as an opaque handle. +typedef struct b2ShapeId +{ + int32_t index1; + uint16_t world0; + uint16_t generation; +} b2ShapeId; + +/// Chain id references a chain instances. This should be treated as an opaque handle. +typedef struct b2ChainId +{ + int32_t index1; + uint16_t world0; + uint16_t generation; +} b2ChainId; + +/// Joint id references a joint instance. This should be treated as an opaque handle. +typedef struct b2JointId +{ + int32_t index1; + uint16_t world0; + uint16_t generation; +} b2JointId; + +/// Contact id references a contact instance. This should be treated as an opaque handled. +typedef struct b2ContactId +{ + int32_t index1; + uint16_t world0; + int16_t padding; + uint32_t generation; +} b2ContactId; + +#ifdef __cplusplus + #define B2_NULL_ID {} + #define B2_ID_INLINE inline +#else + #define B2_NULL_ID { 0 } + #define B2_ID_INLINE static inline +#endif + +/// Use these to make your identifiers null. +/// You may also use zero initialization to get null. +static const b2WorldId b2_nullWorldId = B2_NULL_ID; +static const b2BodyId b2_nullBodyId = B2_NULL_ID; +static const b2ShapeId b2_nullShapeId = B2_NULL_ID; +static const b2ChainId b2_nullChainId = B2_NULL_ID; +static const b2JointId b2_nullJointId = B2_NULL_ID; +static const b2ContactId b2_nullContactId = B2_NULL_ID; + +/// Macro to determine if any id is null. +#define B2_IS_NULL( id ) ( (id).index1 == 0 ) + +/// Macro to determine if any id is non-null. +#define B2_IS_NON_NULL( id ) ( (id).index1 != 0 ) + +/// Compare two ids for equality. Doesn't work for b2WorldId. Don't mix types. +#define B2_ID_EQUALS( id1, id2 ) ( (id1).index1 == (id2).index1 && (id1).world0 == (id2).world0 && (id1).generation == (id2).generation ) + +/// Store a world id into a uint32_t. +B2_ID_INLINE uint32_t b2StoreWorldId( b2WorldId id ) +{ + return ( (uint32_t)id.index1 << 16 ) | (uint32_t)id.generation; +} + +/// Load a uint32_t into a world id. +B2_ID_INLINE b2WorldId b2LoadWorldId( uint32_t x ) +{ + b2WorldId id = { (uint16_t)( x >> 16 ), (uint16_t)( x ) }; + return id; +} + +/// Store a body id into a uint64_t. +B2_ID_INLINE uint64_t b2StoreBodyId( b2BodyId id ) +{ + return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation; +} + +/// Load a uint64_t into a body id. +B2_ID_INLINE b2BodyId b2LoadBodyId( uint64_t x ) +{ + b2BodyId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) }; + return id; +} + +/// Store a shape id into a uint64_t. +B2_ID_INLINE uint64_t b2StoreShapeId( b2ShapeId id ) +{ + return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation; +} + +/// Load a uint64_t into a shape id. +B2_ID_INLINE b2ShapeId b2LoadShapeId( uint64_t x ) +{ + b2ShapeId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) }; + return id; +} + +/// Store a chain id into a uint64_t. +B2_ID_INLINE uint64_t b2StoreChainId( b2ChainId id ) +{ + return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation; +} + +/// Load a uint64_t into a chain id. +B2_ID_INLINE b2ChainId b2LoadChainId( uint64_t x ) +{ + b2ChainId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) }; + return id; +} + +/// Store a joint id into a uint64_t. +B2_ID_INLINE uint64_t b2StoreJointId( b2JointId id ) +{ + return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation; +} + +/// Load a uint64_t into a joint id. +B2_ID_INLINE b2JointId b2LoadJointId( uint64_t x ) +{ + b2JointId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) }; + return id; +} + +/// Store a contact id into 16 bytes +B2_ID_INLINE void b2StoreContactId( b2ContactId id, uint32_t values[3] ) +{ + values[0] = (uint32_t)id.index1; + values[1] = (uint32_t)id.world0; + values[2] = (uint32_t)id.generation; +} + +/// Load a two uint64_t into a contact id. +B2_ID_INLINE b2ContactId b2LoadContactId( uint32_t values[3] ) +{ + b2ContactId id; + id.index1 = (int32_t)values[0]; + id.world0 = (uint16_t)values[1]; + id.padding = 0; + id.generation = (uint32_t)values[2]; + return id; +} + +/**@}*/ diff --git a/libs/box2d/include/box2d/math_functions.h b/libs/box2d/include/box2d/math_functions.h new file mode 100644 index 0000000..ce7382d --- /dev/null +++ b/libs/box2d/include/box2d/math_functions.h @@ -0,0 +1,776 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" + +#include +#include +#include + +/** + * @defgroup math Math + * @brief Vector math types and functions + * @{ + */ + +/// 2D vector +/// This can be used to represent a point or free vector +typedef struct b2Vec2 +{ + /// coordinates + float x, y; +} b2Vec2; + +/// Cosine and sine pair +/// This uses a custom implementation designed for cross-platform determinism +typedef struct b2CosSin +{ + /// cosine and sine + float cosine; + float sine; +} b2CosSin; + +/// 2D rotation +/// This is similar to using a complex number for rotation +typedef struct b2Rot +{ + /// cosine and sine + float c, s; +} b2Rot; + +/// A 2D rigid transform +typedef struct b2Transform +{ + b2Vec2 p; + b2Rot q; +} b2Transform; + +/// A 2-by-2 Matrix +typedef struct b2Mat22 +{ + /// columns + b2Vec2 cx, cy; +} b2Mat22; + +/// Axis-aligned bounding box +typedef struct b2AABB +{ + b2Vec2 lowerBound; + b2Vec2 upperBound; +} b2AABB; + +/// separation = dot(normal, point) - offset +typedef struct b2Plane +{ + b2Vec2 normal; + float offset; +} b2Plane; + +/**@}*/ + +/** + * @addtogroup math + * @{ + */ + +/// https://en.wikipedia.org/wiki/Pi +#define B2_PI 3.14159265359f + +static const b2Vec2 b2Vec2_zero = { 0.0f, 0.0f }; +static const b2Rot b2Rot_identity = { 1.0f, 0.0f }; +static const b2Transform b2Transform_identity = { { 0.0f, 0.0f }, { 1.0f, 0.0f } }; +static const b2Mat22 b2Mat22_zero = { { 0.0f, 0.0f }, { 0.0f, 0.0f } }; + +/// Is this a valid number? Not NaN or infinity. +B2_API bool b2IsValidFloat( float a ); + +/// Is this a valid vector? Not NaN or infinity. +B2_API bool b2IsValidVec2( b2Vec2 v ); + +/// Is this a valid rotation? Not NaN or infinity. Is normalized. +B2_API bool b2IsValidRotation( b2Rot q ); + +/// Is this a valid transform? Not NaN or infinity. Rotation is normalized. +B2_API bool b2IsValidTransform( b2Transform t ); + +/// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound. +B2_API bool b2IsValidAABB( b2AABB aabb ); + +/// Is this a valid plane? Normal is a unit vector. Not Nan or infinity. +B2_API bool b2IsValidPlane( b2Plane a ); + +/// @return the minimum of two integers +B2_INLINE int b2MinInt( int a, int b ) +{ + return a < b ? a : b; +} + +/// @return the maximum of two integers +B2_INLINE int b2MaxInt( int a, int b ) +{ + return a > b ? a : b; +} + +/// @return the absolute value of an integer +B2_INLINE int b2AbsInt( int a ) +{ + return a < 0 ? -a : a; +} + +/// @return an integer clamped between a lower and upper bound +B2_INLINE int b2ClampInt( int a, int lower, int upper ) +{ + return a < lower ? lower : ( a > upper ? upper : a ); +} + +/// @return the minimum of two floats +B2_INLINE float b2MinFloat( float a, float b ) +{ + return a < b ? a : b; +} + +/// @return the maximum of two floats +B2_INLINE float b2MaxFloat( float a, float b ) +{ + return a > b ? a : b; +} + +/// @return the absolute value of a float +B2_INLINE float b2AbsFloat( float a ) +{ + return a < 0 ? -a : a; +} + +/// @return a float clamped between a lower and upper bound +B2_INLINE float b2ClampFloat( float a, float lower, float upper ) +{ + return a < lower ? lower : ( a > upper ? upper : a ); +} + +/// Compute an approximate arctangent in the range [-pi, pi] +/// This is hand coded for cross-platform determinism. The atan2f +/// function in the standard library is not cross-platform deterministic. +/// Accurate to around 0.0023 degrees +B2_API float b2Atan2( float y, float x ); + +/// Compute the cosine and sine of an angle in radians. Implemented +/// for cross-platform determinism. +B2_API b2CosSin b2ComputeCosSin( float radians ); + +/// Vector dot product +B2_INLINE float b2Dot( b2Vec2 a, b2Vec2 b ) +{ + return a.x * b.x + a.y * b.y; +} + +/// Vector cross product. In 2D this yields a scalar. +B2_INLINE float b2Cross( b2Vec2 a, b2Vec2 b ) +{ + return a.x * b.y - a.y * b.x; +} + +/// Perform the cross product on a vector and a scalar. In 2D this produces a vector. +B2_INLINE b2Vec2 b2CrossVS( b2Vec2 v, float s ) +{ + return B2_LITERAL( b2Vec2 ){ s * v.y, -s * v.x }; +} + +/// Perform the cross product on a scalar and a vector. In 2D this produces a vector. +B2_INLINE b2Vec2 b2CrossSV( float s, b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ -s * v.y, s * v.x }; +} + +/// Get a left pointing perpendicular vector. Equivalent to b2CrossSV(1.0f, v) +B2_INLINE b2Vec2 b2LeftPerp( b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ -v.y, v.x }; +} + +/// Get a right pointing perpendicular vector. Equivalent to b2CrossVS(v, 1.0f) +B2_INLINE b2Vec2 b2RightPerp( b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ v.y, -v.x }; +} + +/// Vector addition +B2_INLINE b2Vec2 b2Add( b2Vec2 a, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x + b.x, a.y + b.y }; +} + +/// Vector subtraction +B2_INLINE b2Vec2 b2Sub( b2Vec2 a, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x - b.x, a.y - b.y }; +} + +/// Vector negation +B2_INLINE b2Vec2 b2Neg( b2Vec2 a ) +{ + return B2_LITERAL( b2Vec2 ){ -a.x, -a.y }; +} + +/// Vector linear interpolation +/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ +B2_INLINE b2Vec2 b2Lerp( b2Vec2 a, b2Vec2 b, float t ) +{ + return B2_LITERAL( b2Vec2 ){ ( 1.0f - t ) * a.x + t * b.x, ( 1.0f - t ) * a.y + t * b.y }; +} + +/// Component-wise multiplication +B2_INLINE b2Vec2 b2Mul( b2Vec2 a, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x * b.x, a.y * b.y }; +} + +/// Multiply a scalar and vector +B2_INLINE b2Vec2 b2MulSV( float s, b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ s * v.x, s * v.y }; +} + +/// a + s * b +B2_INLINE b2Vec2 b2MulAdd( b2Vec2 a, float s, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x + s * b.x, a.y + s * b.y }; +} + +/// a - s * b +B2_INLINE b2Vec2 b2MulSub( b2Vec2 a, float s, b2Vec2 b ) +{ + return B2_LITERAL( b2Vec2 ){ a.x - s * b.x, a.y - s * b.y }; +} + +/// Component-wise absolute vector +B2_INLINE b2Vec2 b2Abs( b2Vec2 a ) +{ + b2Vec2 b; + b.x = b2AbsFloat( a.x ); + b.y = b2AbsFloat( a.y ); + return b; +} + +/// Component-wise minimum vector +B2_INLINE b2Vec2 b2Min( b2Vec2 a, b2Vec2 b ) +{ + b2Vec2 c; + c.x = b2MinFloat( a.x, b.x ); + c.y = b2MinFloat( a.y, b.y ); + return c; +} + +/// Component-wise maximum vector +B2_INLINE b2Vec2 b2Max( b2Vec2 a, b2Vec2 b ) +{ + b2Vec2 c; + c.x = b2MaxFloat( a.x, b.x ); + c.y = b2MaxFloat( a.y, b.y ); + return c; +} + +/// Component-wise clamp vector v into the range [a, b] +B2_INLINE b2Vec2 b2Clamp( b2Vec2 v, b2Vec2 a, b2Vec2 b ) +{ + b2Vec2 c; + c.x = b2ClampFloat( v.x, a.x, b.x ); + c.y = b2ClampFloat( v.y, a.y, b.y ); + return c; +} + +/// Get the length of this vector (the norm) +B2_INLINE float b2Length( b2Vec2 v ) +{ + return sqrtf( v.x * v.x + v.y * v.y ); +} + +/// Get the distance between two points +B2_INLINE float b2Distance( b2Vec2 a, b2Vec2 b ) +{ + float dx = b.x - a.x; + float dy = b.y - a.y; + return sqrtf( dx * dx + dy * dy ); +} + +/// Convert a vector into a unit vector if possible, otherwise returns the zero vector. +/// todo MSVC is not inlining this function in several places per warning 4710 +B2_INLINE b2Vec2 b2Normalize( b2Vec2 v ) +{ + float length = sqrtf( v.x * v.x + v.y * v.y ); + if ( length < FLT_EPSILON ) + { + return B2_LITERAL( b2Vec2 ){ 0.0f, 0.0f }; + } + + float invLength = 1.0f / length; + b2Vec2 n = { invLength * v.x, invLength * v.y }; + return n; +} + +/// Determines if the provided vector is normalized (norm(a) == 1). +B2_INLINE bool b2IsNormalized( b2Vec2 a ) +{ + float aa = b2Dot( a, a ); + return b2AbsFloat( 1.0f - aa ) < 100.0f * FLT_EPSILON; +} + +/// Convert a vector into a unit vector if possible, otherwise returns the zero vector. Also +/// outputs the length. +B2_INLINE b2Vec2 b2GetLengthAndNormalize( float* length, b2Vec2 v ) +{ + *length = sqrtf( v.x * v.x + v.y * v.y ); + if ( *length < FLT_EPSILON ) + { + return B2_LITERAL( b2Vec2 ){ 0.0f, 0.0f }; + } + + float invLength = 1.0f / *length; + b2Vec2 n = { invLength * v.x, invLength * v.y }; + return n; +} + +/// Normalize rotation +B2_INLINE b2Rot b2NormalizeRot( b2Rot q ) +{ + float mag = sqrtf( q.s * q.s + q.c * q.c ); + float invMag = mag > 0.0f ? 1.0f / mag : 0.0f; + b2Rot qn = { q.c * invMag, q.s * invMag }; + return qn; +} + +/// Integrate rotation from angular velocity +/// @param q1 initial rotation +/// @param deltaAngle the angular displacement in radians +B2_INLINE b2Rot b2IntegrateRotation( b2Rot q1, float deltaAngle ) +{ + // dc/dt = -omega * sin(t) + // ds/dt = omega * cos(t) + // c2 = c1 - omega * h * s1 + // s2 = s1 + omega * h * c1 + b2Rot q2 = { q1.c - deltaAngle * q1.s, q1.s + deltaAngle * q1.c }; + float mag = sqrtf( q2.s * q2.s + q2.c * q2.c ); + float invMag = mag > 0.0f ? 1.0f / mag : 0.0f; + b2Rot qn = { q2.c * invMag, q2.s * invMag }; + return qn; +} + +/// Get the length squared of this vector +B2_INLINE float b2LengthSquared( b2Vec2 v ) +{ + return v.x * v.x + v.y * v.y; +} + +/// Get the distance squared between points +B2_INLINE float b2DistanceSquared( b2Vec2 a, b2Vec2 b ) +{ + b2Vec2 c = { b.x - a.x, b.y - a.y }; + return c.x * c.x + c.y * c.y; +} + +/// Make a rotation using an angle in radians +B2_INLINE b2Rot b2MakeRot( float radians ) +{ + b2CosSin cs = b2ComputeCosSin( radians ); + return B2_LITERAL( b2Rot ){ cs.cosine, cs.sine }; +} + +/// Make a rotation using a unit vector +B2_INLINE b2Rot b2MakeRotFromUnitVector( b2Vec2 unitVector ) +{ + B2_ASSERT( b2IsNormalized( unitVector ) ); + return B2_LITERAL( b2Rot ){ unitVector.x, unitVector.y }; +} + +/// Compute the rotation between two unit vectors +B2_API b2Rot b2ComputeRotationBetweenUnitVectors( b2Vec2 v1, b2Vec2 v2 ); + +/// Is this rotation normalized? +B2_INLINE bool b2IsNormalizedRot( b2Rot q ) +{ + // larger tolerance due to failure on mingw 32-bit + float qq = q.s * q.s + q.c * q.c; + return 1.0f - 0.0006f < qq && qq < 1.0f + 0.0006f; +} + +/// Get the inverse of a rotation +B2_INLINE b2Rot b2InvertRot( b2Rot a ) +{ + return B2_LITERAL( b2Rot ){ + .c = a.c, + .s = -a.s, + }; +} + +/// Normalized linear interpolation +/// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ +/// https://web.archive.org/web/20170825184056/http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/ +B2_INLINE b2Rot b2NLerp( b2Rot q1, b2Rot q2, float t ) +{ + float omt = 1.0f - t; + b2Rot q = { + omt * q1.c + t * q2.c, + omt * q1.s + t * q2.s, + }; + + float mag = sqrtf( q.s * q.s + q.c * q.c ); + float invMag = mag > 0.0f ? 1.0f / mag : 0.0f; + b2Rot qn = { q.c * invMag, q.s * invMag }; + return qn; +} + +/// Compute the angular velocity necessary to rotate between two rotations over a give time +/// @param q1 initial rotation +/// @param q2 final rotation +/// @param inv_h inverse time step +B2_INLINE float b2ComputeAngularVelocity( b2Rot q1, b2Rot q2, float inv_h ) +{ + // ds/dt = omega * cos(t) + // dc/dt = -omega * sin(t) + // s2 = s1 + omega * h * c1 + // c2 = c1 - omega * h * s1 + + // omega * h * s1 = c1 - c2 + // omega * h * c1 = s2 - s1 + // omega * h = (c1 - c2) * s1 + (s2 - s1) * c1; + // omega * h = s1 * c1 - c2 * s1 + s2 * c1 - s1 * c1 + // omega * h = s2 * c1 - c2 * s1 = sin(a2 - a1) ~= a2 - a1 for small delta + float omega = inv_h * ( q2.s * q1.c - q2.c * q1.s ); + return omega; +} + +/// Get the angle in radians in the range [-pi, pi] +B2_INLINE float b2Rot_GetAngle( b2Rot q ) +{ + return b2Atan2( q.s, q.c ); +} + +/// Get the x-axis +B2_INLINE b2Vec2 b2Rot_GetXAxis( b2Rot q ) +{ + b2Vec2 v = { q.c, q.s }; + return v; +} + +/// Get the y-axis +B2_INLINE b2Vec2 b2Rot_GetYAxis( b2Rot q ) +{ + b2Vec2 v = { -q.s, q.c }; + return v; +} + +/// Multiply two rotations: q * r +B2_INLINE b2Rot b2MulRot( b2Rot q, b2Rot r ) +{ + // [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc] + // [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc] + // s(q + r) = qs * rc + qc * rs + // c(q + r) = qc * rc - qs * rs + b2Rot qr; + qr.s = q.s * r.c + q.c * r.s; + qr.c = q.c * r.c - q.s * r.s; + return qr; +} + +/// Transpose multiply two rotations: inv(a) * b +/// This rotates a vector local in frame b into a vector local in frame a +B2_INLINE b2Rot b2InvMulRot( b2Rot a, b2Rot b ) +{ + // [ ac as] * [bc -bs] = [ac*bc+qs*bs -ac*bs+as*bc] + // [-as ac] [bs bc] [-as*bc+ac*bs as*bs+ac*bc] + // s(a - b) = ac * bs - as * bc + // c(a - b) = ac * bc + as * bs + b2Rot r; + r.s = a.c * b.s - a.s * b.c; + r.c = a.c * b.c + a.s * b.s; + return r; +} + +/// Relative angle between a and b +B2_INLINE float b2RelativeAngle( b2Rot a, b2Rot b ) +{ + // sin(b - a) = bs * ac - bc * as + // cos(b - a) = bc * ac + bs * as + float s = a.c * b.s - a.s * b.c; + float c = a.c * b.c + a.s * b.s; + return b2Atan2( s, c ); +} + +/// Convert any angle into the range [-pi, pi] +B2_INLINE float b2UnwindAngle( float radians ) +{ + // Assuming this is deterministic + return remainderf( radians, 2.0f * B2_PI ); +} + +/// Rotate a vector +B2_INLINE b2Vec2 b2RotateVector( b2Rot q, b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ q.c * v.x - q.s * v.y, q.s * v.x + q.c * v.y }; +} + +/// Inverse rotate a vector +B2_INLINE b2Vec2 b2InvRotateVector( b2Rot q, b2Vec2 v ) +{ + return B2_LITERAL( b2Vec2 ){ q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y }; +} + +/// Transform a point (e.g. local space to world space) +B2_INLINE b2Vec2 b2TransformPoint( b2Transform t, const b2Vec2 p ) +{ + float x = ( t.q.c * p.x - t.q.s * p.y ) + t.p.x; + float y = ( t.q.s * p.x + t.q.c * p.y ) + t.p.y; + + return B2_LITERAL( b2Vec2 ){ x, y }; +} + +/// Inverse transform a point (e.g. world space to local space) +B2_INLINE b2Vec2 b2InvTransformPoint( b2Transform t, const b2Vec2 p ) +{ + float vx = p.x - t.p.x; + float vy = p.y - t.p.y; + return B2_LITERAL( b2Vec2 ){ t.q.c * vx + t.q.s * vy, -t.q.s * vx + t.q.c * vy }; +} + +/// Multiply two transforms. If the result is applied to a point p local to frame B, +/// the transform would first convert p to a point local to frame A, then into a point +/// in the world frame. +/// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p +/// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p +B2_INLINE b2Transform b2MulTransforms( b2Transform A, b2Transform B ) +{ + b2Transform C; + C.q = b2MulRot( A.q, B.q ); + C.p = b2Add( b2RotateVector( A.q, B.p ), A.p ); + return C; +} + +/// Creates a transform that converts a local point in frame B to a local point in frame A. +/// v2 = A.q' * (B.q * v1 + B.p - A.p) +/// = A.q' * B.q * v1 + A.q' * (B.p - A.p) +B2_INLINE b2Transform b2InvMulTransforms( b2Transform A, b2Transform B ) +{ + b2Transform C; + C.q = b2InvMulRot( A.q, B.q ); + C.p = b2InvRotateVector( A.q, b2Sub( B.p, A.p ) ); + return C; +} + +/// Multiply a 2-by-2 matrix times a 2D vector +B2_INLINE b2Vec2 b2MulMV( b2Mat22 A, b2Vec2 v ) +{ + b2Vec2 u = { + A.cx.x * v.x + A.cy.x * v.y, + A.cx.y * v.x + A.cy.y * v.y, + }; + return u; +} + +/// Get the inverse of a 2-by-2 matrix +B2_INLINE b2Mat22 b2GetInverse22( b2Mat22 A ) +{ + float a = A.cx.x, b = A.cy.x, c = A.cx.y, d = A.cy.y; + float det = a * d - b * c; + if ( det != 0.0f ) + { + det = 1.0f / det; + } + + b2Mat22 B = { + { det * d, -det * c }, + { -det * b, det * a }, + }; + return B; +} + +/// Solve A * x = b, where b is a column vector. This is more efficient +/// than computing the inverse in one-shot cases. +B2_INLINE b2Vec2 b2Solve22( b2Mat22 A, b2Vec2 b ) +{ + float a11 = A.cx.x, a12 = A.cy.x, a21 = A.cx.y, a22 = A.cy.y; + float det = a11 * a22 - a12 * a21; + if ( det != 0.0f ) + { + det = 1.0f / det; + } + b2Vec2 x = { det * ( a22 * b.x - a12 * b.y ), det * ( a11 * b.y - a21 * b.x ) }; + return x; +} + +/// Does a fully contain b +B2_INLINE bool b2AABB_Contains( b2AABB a, b2AABB b ) +{ + bool s = true; + s = s && a.lowerBound.x <= b.lowerBound.x; + s = s && a.lowerBound.y <= b.lowerBound.y; + s = s && b.upperBound.x <= a.upperBound.x; + s = s && b.upperBound.y <= a.upperBound.y; + return s; +} + +/// Get the center of the AABB. +B2_INLINE b2Vec2 b2AABB_Center( b2AABB a ) +{ + b2Vec2 b = { 0.5f * ( a.lowerBound.x + a.upperBound.x ), 0.5f * ( a.lowerBound.y + a.upperBound.y ) }; + return b; +} + +/// Get the extents of the AABB (half-widths). +B2_INLINE b2Vec2 b2AABB_Extents( b2AABB a ) +{ + b2Vec2 b = { 0.5f * ( a.upperBound.x - a.lowerBound.x ), 0.5f * ( a.upperBound.y - a.lowerBound.y ) }; + return b; +} + +/// Union of two AABBs +B2_INLINE b2AABB b2AABB_Union( b2AABB a, b2AABB b ) +{ + b2AABB c; + c.lowerBound.x = b2MinFloat( a.lowerBound.x, b.lowerBound.x ); + c.lowerBound.y = b2MinFloat( a.lowerBound.y, b.lowerBound.y ); + c.upperBound.x = b2MaxFloat( a.upperBound.x, b.upperBound.x ); + c.upperBound.y = b2MaxFloat( a.upperBound.y, b.upperBound.y ); + return c; +} + +/// Do a and b overlap +B2_INLINE bool b2AABB_Overlaps( b2AABB a, b2AABB b ) +{ + return !( b.lowerBound.x > a.upperBound.x || b.lowerBound.y > a.upperBound.y || a.lowerBound.x > b.upperBound.x || + a.lowerBound.y > b.upperBound.y ); +} + +/// Compute the bounding box of an array of circles +B2_INLINE b2AABB b2MakeAABB( const b2Vec2* points, int count, float radius ) +{ + B2_ASSERT( count > 0 ); + b2AABB a = { points[0], points[0] }; + for ( int i = 1; i < count; ++i ) + { + a.lowerBound = b2Min( a.lowerBound, points[i] ); + a.upperBound = b2Max( a.upperBound, points[i] ); + } + + b2Vec2 r = { radius, radius }; + a.lowerBound = b2Sub( a.lowerBound, r ); + a.upperBound = b2Add( a.upperBound, r ); + + return a; +} + +/// Signed separation of a point from a plane +B2_INLINE float b2PlaneSeparation( b2Plane plane, b2Vec2 point ) +{ + return b2Dot( plane.normal, point ) - plane.offset; +} + +/// One-dimensional mass-spring-damper simulation. Returns the new velocity given the position and time step. +/// You can then compute the new position using: +/// position += timeStep * newVelocity +/// This drives towards a zero position. By using implicit integration we get a stable solution +/// that doesn't require transcendental functions. +B2_INLINE float b2SpringDamper( float hertz, float dampingRatio, float position, float velocity, float timeStep ) +{ + float omega = 2.0f * B2_PI * hertz; + float omegaH = omega * timeStep; + return ( velocity - omega * omegaH * position ) / ( 1.0f + 2.0f * dampingRatio * omegaH + omegaH * omegaH ); +} + +/// Box2D bases all length units on meters, but you may need different units for your game. +/// You can set this value to use different units. This should be done at application startup +/// and only modified once. Default value is 1. +/// For example, if your game uses pixels for units you can use pixels for all length values +/// sent to Box2D. There should be no extra cost. However, Box2D has some internal tolerances +/// and thresholds that have been tuned for meters. By calling this function, Box2D is able +/// to adjust those tolerances and thresholds to improve accuracy. +/// A good rule of thumb is to pass the height of your player character to this function. So +/// if your player character is 32 pixels high, then pass 32 to this function. Then you may +/// confidently use pixels for all the length values sent to Box2D. All length values returned +/// from Box2D will also be pixels because Box2D does not do any scaling internally. +/// However, you are now on the hook for coming up with good values for gravity, density, and +/// forces. +/// @warning This must be modified before any calls to Box2D +B2_API void b2SetLengthUnitsPerMeter( float lengthUnits ); + +/// Get the current length units per meter. +B2_API float b2GetLengthUnitsPerMeter( void ); + +/**@}*/ + +/** + * @defgroup math_cpp C++ Math + * @brief Math operator overloads for C++ + * + * See math_functions.h for details. + * @{ + */ + +#ifdef __cplusplus + +/// Unary add one vector to another +inline void operator+=( b2Vec2& a, b2Vec2 b ) +{ + a.x += b.x; + a.y += b.y; +} + +/// Unary subtract one vector from another +inline void operator-=( b2Vec2& a, b2Vec2 b ) +{ + a.x -= b.x; + a.y -= b.y; +} + +/// Unary multiply a vector by a scalar +inline void operator*=( b2Vec2& a, float b ) +{ + a.x *= b; + a.y *= b; +} + +/// Unary negate a vector +inline b2Vec2 operator-( b2Vec2 a ) +{ + return { -a.x, -a.y }; +} + +/// Binary vector addition +inline b2Vec2 operator+( b2Vec2 a, b2Vec2 b ) +{ + return { a.x + b.x, a.y + b.y }; +} + +/// Binary vector subtraction +inline b2Vec2 operator-( b2Vec2 a, b2Vec2 b ) +{ + return { a.x - b.x, a.y - b.y }; +} + +/// Binary scalar and vector multiplication +inline b2Vec2 operator*( float a, b2Vec2 b ) +{ + return { a * b.x, a * b.y }; +} + +/// Binary scalar and vector multiplication +inline b2Vec2 operator*( b2Vec2 a, float b ) +{ + return { a.x * b, a.y * b }; +} + +/// Binary vector equality +inline bool operator==( b2Vec2 a, b2Vec2 b ) +{ + return a.x == b.x && a.y == b.y; +} + +/// Binary vector inequality +inline bool operator!=( b2Vec2 a, b2Vec2 b ) +{ + return a.x != b.x || a.y != b.y; +} + +#endif + +/**@}*/ diff --git a/libs/box2d/include/box2d/types.h b/libs/box2d/include/box2d/types.h new file mode 100644 index 0000000..b732a2e --- /dev/null +++ b/libs/box2d/include/box2d/types.h @@ -0,0 +1,1426 @@ +// SPDX-FileCopyrightText: 2023 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" +#include "collision.h" +#include "id.h" +#include "math_functions.h" + +#include +#include + +#define B2_DEFAULT_CATEGORY_BITS 1 +#define B2_DEFAULT_MASK_BITS UINT64_MAX + +/// Task interface +/// This is prototype for a Box2D task. Your task system is expected to invoke the Box2D task with these arguments. +/// The task spans a range of the parallel-for: [startIndex, endIndex) +/// The worker index must correctly identify each worker in the user thread pool, expected in [0, workerCount). +/// A worker must only exist on only one thread at a time and is analogous to the thread index. +/// The task context is the context pointer sent from Box2D when it is enqueued. +/// The startIndex and endIndex are expected in the range [0, itemCount) where itemCount is the argument to b2EnqueueTaskCallback +/// below. Box2D expects startIndex < endIndex and will execute a loop like this: +/// +/// @code{.c} +/// for (int i = startIndex; i < endIndex; ++i) +/// { +/// DoWork(); +/// } +/// @endcode +/// @ingroup world +typedef void b2TaskCallback( int startIndex, int endIndex, uint32_t workerIndex, void* taskContext ); + +/// These functions can be provided to Box2D to invoke a task system. These are designed to work well with enkiTS. +/// Returns a pointer to the user's task object. May be nullptr. A nullptr indicates to Box2D that the work was executed +/// serially within the callback and there is no need to call b2FinishTaskCallback. +/// The itemCount is the number of Box2D work items that are to be partitioned among workers by the user's task system. +/// This is essentially a parallel-for. The minRange parameter is a suggestion of the minimum number of items to assign +/// per worker to reduce overhead. For example, suppose the task is small and that itemCount is 16. A minRange of 8 suggests +/// that your task system should split the work items among just two workers, even if you have more available. +/// In general the range [startIndex, endIndex) send to b2TaskCallback should obey: +/// endIndex - startIndex >= minRange +/// The exception of course is when itemCount < minRange. +/// @ingroup world +typedef void* b2EnqueueTaskCallback( b2TaskCallback* task, int itemCount, int minRange, void* taskContext, void* userContext ); + +/// Finishes a user task object that wraps a Box2D task. +/// @ingroup world +typedef void b2FinishTaskCallback( void* userTask, void* userContext ); + +/// Optional friction mixing callback. This intentionally provides no context objects because this is called +/// from a worker thread. +/// @warning This function should not attempt to modify Box2D state or user application state. +/// @ingroup world +typedef float b2FrictionCallback( float frictionA, uint64_t userMaterialIdA, float frictionB, uint64_t userMaterialIdB ); + +/// Optional restitution mixing callback. This intentionally provides no context objects because this is called +/// from a worker thread. +/// @warning This function should not attempt to modify Box2D state or user application state. +/// @ingroup world +typedef float b2RestitutionCallback( float restitutionA, uint64_t userMaterialIdA, float restitutionB, uint64_t userMaterialIdB ); + +/// Result from b2World_RayCastClosest +/// If there is initial overlap the fraction and normal will be zero while the point is an arbitrary point in the overlap region. +/// @ingroup world +typedef struct b2RayResult +{ + b2ShapeId shapeId; + b2Vec2 point; + b2Vec2 normal; + float fraction; + int nodeVisits; + int leafVisits; + bool hit; +} b2RayResult; + +/// World definition used to create a simulation world. +/// Must be initialized using b2DefaultWorldDef(). +/// @ingroup world +typedef struct b2WorldDef +{ + /// Gravity vector. Box2D has no up-vector defined. + b2Vec2 gravity; + + /// Restitution speed threshold, usually in m/s. Collisions above this + /// speed have restitution applied (will bounce). + float restitutionThreshold; + + /// Threshold speed for hit events. Usually meters per second. + float hitEventThreshold; + + /// Contact stiffness. Cycles per second. Increasing this increases the speed of overlap recovery, but can introduce jitter. + float contactHertz; + + /// Contact bounciness. Non-dimensional. You can speed up overlap recovery by decreasing this with + /// the trade-off that overlap resolution becomes more energetic. + float contactDampingRatio; + + /// This parameter controls how fast overlap is resolved and usually has units of meters per second. This only + /// puts a cap on the resolution speed. The resolution speed is increased by increasing the hertz and/or + /// decreasing the damping ratio. + float contactSpeed; + + /// Maximum linear speed. Usually meters per second. + float maximumLinearSpeed; + + /// Optional mixing callback for friction. The default uses sqrt(frictionA * frictionB). + b2FrictionCallback* frictionCallback; + + /// Optional mixing callback for restitution. The default uses max(restitutionA, restitutionB). + b2RestitutionCallback* restitutionCallback; + + /// Can bodies go to sleep to improve performance + bool enableSleep; + + /// Enable continuous collision + bool enableContinuous; + + /// Contact softening when mass ratios are large. Experimental. + bool enableContactSoftening; + + /// Number of workers to use with the provided task system. Box2D performs best when using only + /// performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide + /// little benefit and may even harm performance. + /// @note Box2D does not create threads. This is the number of threads your applications has created + /// that you are allocating to b2World_Step. + /// @warning Do not modify the default value unless you are also providing a task system and providing + /// task callbacks (enqueueTask and finishTask). + int workerCount; + + /// Function to spawn tasks + b2EnqueueTaskCallback* enqueueTask; + + /// Function to finish a task + b2FinishTaskCallback* finishTask; + + /// User context that is provided to enqueueTask and finishTask + void* userTaskContext; + + /// User data + void* userData; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2WorldDef; + +/// Use this to initialize your world definition +/// @ingroup world +B2_API b2WorldDef b2DefaultWorldDef( void ); + +/// The body simulation type. +/// Each body is one of these three types. The type determines how the body behaves in the simulation. +/// @ingroup body +typedef enum b2BodyType +{ + /// zero mass, zero velocity, may be manually moved + b2_staticBody = 0, + + /// zero mass, velocity set by user, moved by solver + b2_kinematicBody = 1, + + /// positive mass, velocity determined by forces, moved by solver + b2_dynamicBody = 2, + + /// number of body types + b2_bodyTypeCount, +} b2BodyType; + +/// Motion locks to restrict the body movement +typedef struct b2MotionLocks +{ + /// Prevent translation along the x-axis + bool linearX; + + /// Prevent translation along the y-axis + bool linearY; + + /// Prevent rotation around the z-axis + bool angularZ; +} b2MotionLocks; + +/// A body definition holds all the data needed to construct a rigid body. +/// You can safely re-use body definitions. Shapes are added to a body after construction. +/// Body definitions are temporary objects used to bundle creation parameters. +/// Must be initialized using b2DefaultBodyDef(). +/// @ingroup body +typedef struct b2BodyDef +{ + /// The body type: static, kinematic, or dynamic. + b2BodyType type; + + /// The initial world position of the body. Bodies should be created with the desired position. + /// @note Creating bodies at the origin and then moving them nearly doubles the cost of body creation, especially + /// if the body is moved after shapes have been added. + b2Vec2 position; + + /// The initial world rotation of the body. Use b2MakeRot() if you have an angle. + b2Rot rotation; + + /// The initial linear velocity of the body's origin. Usually in meters per second. + b2Vec2 linearVelocity; + + /// The initial angular velocity of the body. Radians per second. + float angularVelocity; + + /// Linear damping is used to reduce the linear velocity. The damping parameter + /// can be larger than 1 but the damping effect becomes sensitive to the + /// time step when the damping parameter is large. + /// Generally linear damping is undesirable because it makes objects move slowly + /// as if they are floating. + float linearDamping; + + /// Angular damping is used to reduce the angular velocity. The damping parameter + /// can be larger than 1.0f but the damping effect becomes sensitive to the + /// time step when the damping parameter is large. + /// Angular damping can be use slow down rotating bodies. + float angularDamping; + + /// Scale the gravity applied to this body. Non-dimensional. + float gravityScale; + + /// Sleep speed threshold, default is 0.05 meters per second + float sleepThreshold; + + /// Optional body name for debugging. Up to 31 characters (excluding null termination) + const char* name; + + /// Use this to store application specific body data. + void* userData; + + /// Motions locks to restrict linear and angular movement. + /// Caution: may lead to softer constraints along the locked direction + b2MotionLocks motionLocks; + + /// Set this flag to false if this body should never fall asleep. + bool enableSleep; + + /// Is this body initially awake or sleeping? + bool isAwake; + + /// Treat this body as a high speed object that performs continuous collision detection + /// against dynamic and kinematic bodies, but not other bullet bodies. + /// @warning Bullets should be used sparingly. They are not a solution for general dynamic-versus-dynamic + /// continuous collision. They do not guarantee accurate collision if both bodies are fast moving because + /// the bullet does a continuous check after all non-bullet bodies have moved. You could get unlucky and have + /// the bullet body end a time step very close to a non-bullet body and the non-bullet body then moves over + /// the bullet body. In continuous collision, initial overlap is ignored to avoid freezing bodies in place. + /// I do not recommend using them for game projectiles if precise collision timing is needed. Instead consider + /// using a ray or shape cast. You can use a marching ray or shape cast for projectile that moves over time. + /// If you want a fast moving projectile to collide with a fast moving target, you need to consider the relative + /// movement in your ray or shape cast. This is out of the scope of Box2D. + /// So what are good use cases for bullets? Pinball games or games with dynamic containers that hold other objects. + /// It should be a use case where it doesn't break the game if there is a collision missed, but the having them + /// captured improves the quality of the game. + bool isBullet; + + /// Used to disable a body. A disabled body does not move or collide. + bool isEnabled; + + /// This allows this body to bypass rotational speed limits. Should only be used + /// for circular objects, like wheels. + bool allowFastRotation; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2BodyDef; + +/// Use this to initialize your body definition +/// @ingroup body +B2_API b2BodyDef b2DefaultBodyDef( void ); + +/// This is used to filter collision on shapes. It affects shape-vs-shape collision +/// and shape-versus-query collision (such as b2World_CastRay). +/// @ingroup shape +typedef struct b2Filter +{ + /// The collision category bits. Normally you would just set one bit. The category bits should + /// represent your application object types. For example: + /// @code{.cpp} + /// enum MyCategories + /// { + /// Static = 0x00000001, + /// Dynamic = 0x00000002, + /// Debris = 0x00000004, + /// Player = 0x00000008, + /// // etc + /// }; + /// @endcode + uint64_t categoryBits; + + /// The collision mask bits. This states the categories that this + /// shape would accept for collision. + /// For example, you may want your player to only collide with static objects + /// and other players. + /// @code{.c} + /// maskBits = Static | Player; + /// @endcode + uint64_t maskBits; + + /// Collision groups allow a certain group of objects to never collide (negative) + /// or always collide (positive). A group index of zero has no effect. Non-zero group filtering + /// always wins against the mask bits. + /// For example, you may want ragdolls to collide with other ragdolls but you don't want + /// ragdoll self-collision. In this case you would give each ragdoll a unique negative group index + /// and apply that group index to all shapes on the ragdoll. + int groupIndex; +} b2Filter; + +/// Use this to initialize your filter +/// @ingroup shape +B2_API b2Filter b2DefaultFilter( void ); + +/// The query filter is used to filter collisions between queries and shapes. For example, +/// you may want a ray-cast representing a projectile to hit players and the static environment +/// but not debris. +/// @ingroup shape +typedef struct b2QueryFilter +{ + /// The collision category bits of this query. Normally you would just set one bit. + uint64_t categoryBits; + + /// The collision mask bits. This states the shape categories that this + /// query would accept for collision. + uint64_t maskBits; +} b2QueryFilter; + +/// Use this to initialize your query filter +/// @ingroup shape +B2_API b2QueryFilter b2DefaultQueryFilter( void ); + +/// Shape type +/// @ingroup shape +typedef enum b2ShapeType +{ + /// A circle with an offset + b2_circleShape, + + /// A capsule is an extruded circle + b2_capsuleShape, + + /// A line segment + b2_segmentShape, + + /// A convex polygon + b2_polygonShape, + + /// A line segment owned by a chain shape + b2_chainSegmentShape, + + /// The number of shape types + b2_shapeTypeCount +} b2ShapeType; + +/// Surface materials allow chain shapes to have per segment surface properties. +/// @ingroup shape +typedef struct b2SurfaceMaterial +{ + /// The Coulomb (dry) friction coefficient, usually in the range [0,1]. + float friction; + + /// The coefficient of restitution (bounce) usually in the range [0,1]. + /// https://en.wikipedia.org/wiki/Coefficient_of_restitution + float restitution; + + /// The rolling resistance usually in the range [0,1]. + float rollingResistance; + + /// The tangent speed for conveyor belts + float tangentSpeed; + + /// User material identifier. This is passed with query results and to friction and restitution + /// combining functions. It is not used internally. + uint64_t userMaterialId; + + /// Custom debug draw color. + uint32_t customColor; +} b2SurfaceMaterial; + +/// Use this to initialize your surface material +/// @ingroup shape +B2_API b2SurfaceMaterial b2DefaultSurfaceMaterial( void ); + +/// Used to create a shape. +/// This is a temporary object used to bundle shape creation parameters. You may use +/// the same shape definition to create multiple shapes. +/// Must be initialized using b2DefaultShapeDef(). +/// @ingroup shape +typedef struct b2ShapeDef +{ + /// Use this to store application specific shape data. + void* userData; + + /// The surface material for this shape. + b2SurfaceMaterial material; + + /// The density, usually in kg/m^2. + /// This is not part of the surface material because this is for the interior, which may have + /// other considerations, such as being hollow. For example a wood barrel may be hollow or full of water. + float density; + + /// Collision filtering data. + b2Filter filter; + + /// Enable custom filtering. Only one of the two shapes needs to enable custom filtering. See b2WorldDef. + bool enableCustomFiltering; + + /// A sensor shape generates overlap events but never generates a collision response. + /// Sensors do not have continuous collision. Instead, use a ray or shape cast for those scenarios. + /// Sensors still contribute to the body mass if they have non-zero density. + /// @note Sensor events are disabled by default. + /// @see enableSensorEvents + bool isSensor; + + /// Enable sensor events for this shape. This applies to sensors and non-sensors. False by default, even for sensors. + bool enableSensorEvents; + + /// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default. + bool enableContactEvents; + + /// Enable hit events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default. + bool enableHitEvents; + + /// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive + /// and must be carefully handled due to multithreading. Ignored for sensors. + bool enablePreSolveEvents; + + /// When shapes are created they will scan the environment for collision the next time step. This can significantly slow down + /// static body creation when there are many static shapes. + /// This is flag is ignored for dynamic and kinematic shapes which always invoke contact creation. + bool invokeContactCreation; + + /// Should the body update the mass properties when this shape is created. Default is true. + /// Warning: if this is true, you MUST call b2Body_ApplyMassFromShapes before simulating the world. + bool updateBodyMass; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2ShapeDef; + +/// Use this to initialize your shape definition +/// @ingroup shape +B2_API b2ShapeDef b2DefaultShapeDef( void ); + +/// Used to create a chain of line segments. This is designed to eliminate ghost collisions with some limitations. +/// - chains are one-sided +/// - chains have no mass and should be used on static bodies +/// - chains have a counter-clockwise winding order (normal points right of segment direction) +/// - chains are either a loop or open +/// - a chain must have at least 4 points +/// - the distance between any two points must be greater than B2_LINEAR_SLOP +/// - a chain shape should not self intersect (this is not validated) +/// - an open chain shape has NO COLLISION on the first and final edge +/// - you may overlap two open chains on their first three and/or last three points to get smooth collision +/// - a chain shape creates multiple line segment shapes on the body +/// https://en.wikipedia.org/wiki/Polygonal_chain +/// Must be initialized using b2DefaultChainDef(). +/// @warning Do not use chain shapes unless you understand the limitations. This is an advanced feature. +/// @ingroup shape +typedef struct b2ChainDef +{ + /// Use this to store application specific shape data. + void* userData; + + /// An array of at least 4 points. These are cloned and may be temporary. + const b2Vec2* points; + + /// The point count, must be 4 or more. + int count; + + /// Surface materials for each segment. These are cloned. + const b2SurfaceMaterial* materials; + + /// The material count. Must be 1 or count. This allows you to provide one + /// material for all segments or a unique material per segment. For open + /// chains, the material on the ghost segments are place holders. + int materialCount; + + /// Contact filtering data. + b2Filter filter; + + /// Indicates a closed chain formed by connecting the first and last points + bool isLoop; + + /// Enable sensors to detect this chain. False by default. + bool enableSensorEvents; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2ChainDef; + +/// Use this to initialize your chain definition +/// @ingroup shape +B2_API b2ChainDef b2DefaultChainDef( void ); + +//! @cond +/// Profiling data. Times are in milliseconds. +typedef struct b2Profile +{ + float step; + float pairs; + float collide; + float solve; + float prepareStages; + float solveConstraints; + float prepareConstraints; + float integrateVelocities; + float warmStart; + float solveImpulses; + float integratePositions; + float relaxImpulses; + float applyRestitution; + float storeImpulses; + float splitIslands; + float transforms; + float sensorHits; + float jointEvents; + float hitEvents; + float refit; + float bullets; + float sleepIslands; + float sensors; +} b2Profile; + +/// Counters that give details of the simulation size. +typedef struct b2Counters +{ + int bodyCount; + int shapeCount; + int contactCount; + int jointCount; + int islandCount; + int stackUsed; + int staticTreeHeight; + int treeHeight; + int byteCount; + int taskCount; + int colorCounts[24]; +} b2Counters; +//! @endcond + +/// Joint type enumeration +/// +/// This is useful because all joint types use b2JointId and sometimes you +/// want to get the type of a joint. +/// @ingroup joint +typedef enum b2JointType +{ + b2_distanceJoint, + b2_filterJoint, + b2_motorJoint, + b2_prismaticJoint, + b2_revoluteJoint, + b2_weldJoint, + b2_wheelJoint, +} b2JointType; + +/// Base joint definition used by all joint types. +/// The local frames are measured from the body's origin rather than the center of mass because: +/// 1. you might not know where the center of mass will be +/// 2. if you add/remove shapes from a body and recompute the mass, the joints will be broken +typedef struct b2JointDef +{ + /// User data pointer + void* userData; + + /// The first attached body + b2BodyId bodyIdA; + + /// The second attached body + b2BodyId bodyIdB; + + /// The first local joint frame + b2Transform localFrameA; + + /// The second local joint frame + b2Transform localFrameB; + + /// Force threshold for joint events + float forceThreshold; + + /// Torque threshold for joint events + float torqueThreshold; + + /// Constraint hertz (advanced feature) + float constraintHertz; + + /// Constraint damping ratio (advanced feature) + float constraintDampingRatio; + + /// Debug draw scale + float drawScale; + + /// Set this flag to true if the attached bodies should collide + bool collideConnected; + +} b2JointDef; + +/// Distance joint definition +/// Connects a point on body A with a point on body B by a segment. +/// Useful for ropes and springs. +/// @ingroup distance_joint +typedef struct b2DistanceJointDef +{ + /// Base joint definition + b2JointDef base; + + /// The rest length of this joint. Clamped to a stable minimum value. + float length; + + /// Enable the distance constraint to behave like a spring. If false + /// then the distance joint will be rigid, overriding the limit and motor. + bool enableSpring; + + /// The lower spring force controls how much tension it can sustain + float lowerSpringForce; + + /// The upper spring force controls how much compression it an sustain + float upperSpringForce; + + /// The spring linear stiffness Hertz, cycles per second + float hertz; + + /// The spring linear damping ratio, non-dimensional + float dampingRatio; + + /// Enable/disable the joint limit + bool enableLimit; + + /// Minimum length for limit. Clamped to a stable minimum value. + float minLength; + + /// Maximum length for limit. Must be greater than or equal to the minimum length. + float maxLength; + + /// Enable/disable the joint motor + bool enableMotor; + + /// The maximum motor force, usually in newtons + float maxMotorForce; + + /// The desired motor speed, usually in meters per second + float motorSpeed; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2DistanceJointDef; + +/// Use this to initialize your joint definition +/// @ingroup distance_joint +B2_API b2DistanceJointDef b2DefaultDistanceJointDef( void ); + +/// A motor joint is used to control the relative velocity and or transform between two bodies. +/// With a velocity of zero this acts like top-down friction. +/// @ingroup motor_joint +typedef struct b2MotorJointDef +{ + /// Base joint definition + b2JointDef base; + + /// The desired linear velocity + b2Vec2 linearVelocity; + + /// The maximum motor force in newtons + float maxVelocityForce; + + /// The desired angular velocity + float angularVelocity; + + /// The maximum motor torque in newton-meters + float maxVelocityTorque; + + /// Linear spring hertz for position control + float linearHertz; + + /// Linear spring damping ratio + float linearDampingRatio; + + /// Maximum spring force in newtons + float maxSpringForce; + + /// Angular spring hertz for position control + float angularHertz; + + /// Angular spring damping ratio + float angularDampingRatio; + + /// Maximum spring torque in newton-meters + float maxSpringTorque; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2MotorJointDef; + +/// Use this to initialize your joint definition +/// @ingroup motor_joint +B2_API b2MotorJointDef b2DefaultMotorJointDef( void ); + +/// A filter joint is used to disable collision between two specific bodies. +/// +/// @ingroup filter_joint +typedef struct b2FilterJointDef +{ + /// Base joint definition + b2JointDef base; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2FilterJointDef; + +/// Use this to initialize your joint definition +/// @ingroup filter_joint +B2_API b2FilterJointDef b2DefaultFilterJointDef( void ); + +/// Prismatic joint definition +/// Body B may slide along the x-axis in local frame A. Body B cannot rotate relative to body A. +/// The joint translation is zero when the local frame origins coincide in world space. +/// @ingroup prismatic_joint +typedef struct b2PrismaticJointDef +{ + /// Base joint definition + b2JointDef base; + + /// Enable a linear spring along the prismatic joint axis + bool enableSpring; + + /// The spring stiffness Hertz, cycles per second + float hertz; + + /// The spring damping ratio, non-dimensional + float dampingRatio; + + /// The target translation for the joint in meters. The spring-damper will drive + /// to this translation. + float targetTranslation; + + /// Enable/disable the joint limit + bool enableLimit; + + /// The lower translation limit + float lowerTranslation; + + /// The upper translation limit + float upperTranslation; + + /// Enable/disable the joint motor + bool enableMotor; + + /// The maximum motor force, typically in newtons + float maxMotorForce; + + /// The desired motor speed, typically in meters per second + float motorSpeed; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2PrismaticJointDef; + +/// Use this to initialize your joint definition +/// @ingroup prismatic_joint +B2_API b2PrismaticJointDef b2DefaultPrismaticJointDef( void ); + +/// Revolute joint definition +/// A point on body B is fixed to a point on body A. Allows relative rotation. +/// @ingroup revolute_joint +typedef struct b2RevoluteJointDef +{ + /// Base joint definition + b2JointDef base; + + /// The target angle for the joint in radians. The spring-damper will drive + /// to this angle. + float targetAngle; + + /// Enable a rotational spring on the revolute hinge axis + bool enableSpring; + + /// The spring stiffness Hertz, cycles per second + float hertz; + + /// The spring damping ratio, non-dimensional + float dampingRatio; + + /// A flag to enable joint limits + bool enableLimit; + + /// The lower angle for the joint limit in radians. Minimum of -0.99*pi radians. + float lowerAngle; + + /// The upper angle for the joint limit in radians. Maximum of 0.99*pi radians. + float upperAngle; + + /// A flag to enable the joint motor + bool enableMotor; + + /// The maximum motor torque, typically in newton-meters + float maxMotorTorque; + + /// The desired motor speed in radians per second + float motorSpeed; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2RevoluteJointDef; + +/// Use this to initialize your joint definition. +/// @ingroup revolute_joint +B2_API b2RevoluteJointDef b2DefaultRevoluteJointDef( void ); + +/// Weld joint definition +/// Connects two bodies together rigidly. This constraint provides springs to mimic +/// soft-body simulation. +/// @note The approximate solver in Box2D cannot hold many bodies together rigidly +/// @ingroup weld_joint +typedef struct b2WeldJointDef +{ + /// Base joint definition + b2JointDef base; + + /// Linear stiffness expressed as Hertz (cycles per second). Use zero for maximum stiffness. + float linearHertz; + + /// Angular stiffness as Hertz (cycles per second). Use zero for maximum stiffness. + float angularHertz; + + /// Linear damping ratio, non-dimensional. Use 1 for critical damping. + float linearDampingRatio; + + /// Linear damping ratio, non-dimensional. Use 1 for critical damping. + float angularDampingRatio; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2WeldJointDef; + +/// Use this to initialize your joint definition +/// @ingroup weld_joint +B2_API b2WeldJointDef b2DefaultWeldJointDef( void ); + +/// Wheel joint definition +/// Body B is a wheel that may rotate freely and slide along the local x-axis in frame A. +/// The joint translation is zero when the local frame origins coincide in world space. +/// @ingroup wheel_joint +typedef struct b2WheelJointDef +{ + /// Base joint definition + b2JointDef base; + + /// Enable a linear spring along the local axis + bool enableSpring; + + /// Spring stiffness in Hertz + float hertz; + + /// Spring damping ratio, non-dimensional + float dampingRatio; + + /// Enable/disable the joint linear limit + bool enableLimit; + + /// The lower translation limit + float lowerTranslation; + + /// The upper translation limit + float upperTranslation; + + /// Enable/disable the joint rotational motor + bool enableMotor; + + /// The maximum motor torque, typically in newton-meters + float maxMotorTorque; + + /// The desired motor speed in radians per second + float motorSpeed; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b2WheelJointDef; + +/// Use this to initialize your joint definition +/// @ingroup wheel_joint +B2_API b2WheelJointDef b2DefaultWheelJointDef( void ); + +/// The explosion definition is used to configure options for explosions. Explosions +/// consider shape geometry when computing the impulse. +/// @ingroup world +typedef struct b2ExplosionDef +{ + /// Mask bits to filter shapes + uint64_t maskBits; + + /// The center of the explosion in world space + b2Vec2 position; + + /// The radius of the explosion + float radius; + + /// The falloff distance beyond the radius. Impulse is reduced to zero at this distance. + float falloff; + + /// Impulse per unit length. This applies an impulse according to the shape perimeter that + /// is facing the explosion. Explosions only apply to circles, capsules, and polygons. This + /// may be negative for implosions. + float impulsePerLength; +} b2ExplosionDef; + +/// Use this to initialize your explosion definition +/// @ingroup world +B2_API b2ExplosionDef b2DefaultExplosionDef( void ); + +/** + * @defgroup events Events + * World event types. + * + * Events are used to collect events that occur during the world time step. These events + * are then available to query after the time step is complete. This is preferable to callbacks + * because Box2D uses multithreaded simulation. + * + * Also when events occur in the simulation step it may be problematic to modify the world, which is + * often what applications want to do when events occur. + * + * With event arrays, you can scan the events in a loop and modify the world. However, you need to be careful + * that some event data may become invalid. There are several samples that show how to do this safely. + * + * @{ + */ + +/// A begin touch event is generated when a shape starts to overlap a sensor shape. +typedef struct b2SensorBeginTouchEvent +{ + /// The id of the sensor shape + b2ShapeId sensorShapeId; + + /// The id of the shape that began touching the sensor shape + b2ShapeId visitorShapeId; +} b2SensorBeginTouchEvent; + +/// An end touch event is generated when a shape stops overlapping a sensor shape. +/// These include things like setting the transform, destroying a body or shape, or changing +/// a filter. You will also get an end event if the sensor or visitor are destroyed. +/// Therefore you should always confirm the shape id is valid using b2Shape_IsValid. +typedef struct b2SensorEndTouchEvent +{ + /// The id of the sensor shape + /// @warning this shape may have been destroyed + /// @see b2Shape_IsValid + b2ShapeId sensorShapeId; + + /// The id of the shape that stopped touching the sensor shape + /// @warning this shape may have been destroyed + /// @see b2Shape_IsValid + b2ShapeId visitorShapeId; + +} b2SensorEndTouchEvent; + +/// Sensor events are buffered in the world and are available +/// as begin/end overlap event arrays after the time step is complete. +/// Note: these may become invalid if bodies and/or shapes are destroyed +typedef struct b2SensorEvents +{ + /// Array of sensor begin touch events + b2SensorBeginTouchEvent* beginEvents; + + /// Array of sensor end touch events + b2SensorEndTouchEvent* endEvents; + + /// The number of begin touch events + int beginCount; + + /// The number of end touch events + int endCount; +} b2SensorEvents; + +/// A begin touch event is generated when two shapes begin touching. +typedef struct b2ContactBeginTouchEvent +{ + /// Id of the first shape + b2ShapeId shapeIdA; + + /// Id of the second shape + b2ShapeId shapeIdB; + + /// The transient contact id. This contact maybe destroyed automatically when the world is modified or simulated. + /// Used b2Contact_IsValid before using this id. + b2ContactId contactId; +} b2ContactBeginTouchEvent; + +/// An end touch event is generated when two shapes stop touching. +/// You will get an end event if you do anything that destroys contacts previous to the last +/// world step. These include things like setting the transform, destroying a body +/// or shape, or changing a filter or body type. +typedef struct b2ContactEndTouchEvent +{ + /// Id of the first shape + /// @warning this shape may have been destroyed + /// @see b2Shape_IsValid + b2ShapeId shapeIdA; + + /// Id of the second shape + /// @warning this shape may have been destroyed + /// @see b2Shape_IsValid + b2ShapeId shapeIdB; + + /// Id of the contact. + /// @warning this contact may have been destroyed + /// @see b2Contact_IsValid + b2ContactId contactId; +} b2ContactEndTouchEvent; + +/// A hit touch event is generated when two shapes collide with a speed faster than the hit speed threshold. +/// This may be reported for speculative contacts that have a confirmed impulse. +typedef struct b2ContactHitEvent +{ + /// Id of the first shape + b2ShapeId shapeIdA; + + /// Id of the second shape + b2ShapeId shapeIdB; + + /// Id of the contact. + /// @warning this contact may have been destroyed + /// @see b2Contact_IsValid + b2ContactId contactId; + + /// Point where the shapes hit at the beginning of the time step. + /// This is a mid-point between the two surfaces. It could be at speculative + /// point where the two shapes were not touching at the beginning of the time step. + b2Vec2 point; + + /// Normal vector pointing from shape A to shape B + b2Vec2 normal; + + /// The speed the shapes are approaching. Always positive. Typically in meters per second. + float approachSpeed; +} b2ContactHitEvent; + +/// Contact events are buffered in the Box2D world and are available +/// as event arrays after the time step is complete. +/// Note: these may become invalid if bodies and/or shapes are destroyed +typedef struct b2ContactEvents +{ + /// Array of begin touch events + b2ContactBeginTouchEvent* beginEvents; + + /// Array of end touch events + b2ContactEndTouchEvent* endEvents; + + /// Array of hit events + b2ContactHitEvent* hitEvents; + + /// Number of begin touch events + int beginCount; + + /// Number of end touch events + int endCount; + + /// Number of hit events + int hitCount; +} b2ContactEvents; + +/// Body move events triggered when a body moves. +/// Triggered when a body moves due to simulation. Not reported for bodies moved by the user. +/// This also has a flag to indicate that the body went to sleep so the application can also +/// sleep that actor/entity/object associated with the body. +/// On the other hand if the flag does not indicate the body went to sleep then the application +/// can treat the actor/entity/object associated with the body as awake. +/// This is an efficient way for an application to update game object transforms rather than +/// calling functions such as b2Body_GetTransform() because this data is delivered as a contiguous array +/// and it is only populated with bodies that have moved. +/// @note If sleeping is disabled all dynamic and kinematic bodies will trigger move events. +typedef struct b2BodyMoveEvent +{ + void* userData; + b2Transform transform; + b2BodyId bodyId; + bool fellAsleep; +} b2BodyMoveEvent; + +/// Body events are buffered in the Box2D world and are available +/// as event arrays after the time step is complete. +/// Note: this data becomes invalid if bodies are destroyed +typedef struct b2BodyEvents +{ + /// Array of move events + b2BodyMoveEvent* moveEvents; + + /// Number of move events + int moveCount; +} b2BodyEvents; + +/// Joint events report joints that are awake and have a force and/or torque exceeding the threshold +/// The observed forces and torques are not returned for efficiency reasons. +typedef struct b2JointEvent +{ + /// The joint id + b2JointId jointId; + + /// The user data from the joint for convenience + void* userData; +} b2JointEvent; + +/// Joint events are buffered in the world and are available +/// as event arrays after the time step is complete. +/// Note: this data becomes invalid if joints are destroyed +typedef struct b2JointEvents +{ + /// Array of events + b2JointEvent* jointEvents; + + /// Number of events + int count; +} b2JointEvents; + +/// The contact data for two shapes. By convention the manifold normal points +/// from shape A to shape B. +/// @see b2Shape_GetContactData() and b2Body_GetContactData() +typedef struct b2ContactData +{ + b2ContactId contactId; + b2ShapeId shapeIdA; + b2ShapeId shapeIdB; + b2Manifold manifold; +} b2ContactData; + +/**@}*/ + +/// Prototype for a contact filter callback. +/// This is called when a contact pair is considered for collision. This allows you to +/// perform custom logic to prevent collision between shapes. This is only called if +/// one of the two shapes has custom filtering enabled. +/// Notes: +/// - this function must be thread-safe +/// - this is only called if one of the two shapes has enabled custom filtering +/// - this may be called for awake dynamic bodies and sensors +/// Return false if you want to disable the collision +/// @see b2ShapeDef +/// @warning Do not attempt to modify the world inside this callback +/// @ingroup world +typedef bool b2CustomFilterFcn( b2ShapeId shapeIdA, b2ShapeId shapeIdB, void* context ); + +/// Prototype for a pre-solve callback. +/// This is called after a contact is updated. This allows you to inspect a +/// contact before it goes to the solver. If you are careful, you can modify the +/// contact manifold (e.g. modify the normal). +/// Notes: +/// - this function must be thread-safe +/// - this is only called if the shape has enabled pre-solve events +/// - this is called only for awake dynamic bodies +/// - this is not called for sensors +/// - the supplied manifold has impulse values from the previous step +/// Return false if you want to disable the contact this step +/// @warning Do not attempt to modify the world inside this callback +/// @ingroup world +typedef bool b2PreSolveFcn( b2ShapeId shapeIdA, b2ShapeId shapeIdB, b2Vec2 point, b2Vec2 normal, void* context ); + +/// Prototype callback for overlap queries. +/// Called for each shape found in the query. +/// @see b2World_OverlapABB +/// @return false to terminate the query. +/// @ingroup world +typedef bool b2OverlapResultFcn( b2ShapeId shapeId, void* context ); + +/// Prototype callback for ray and shape casts. +/// Called for each shape found in the query. You control how the ray cast +/// proceeds by returning a float: +/// return -1: ignore this shape and continue +/// return 0: terminate the ray cast +/// return fraction: clip the ray to this point +/// return 1: don't clip the ray and continue +/// A cast with initial overlap will return a zero fraction and a zero normal. +/// @param shapeId the shape hit by the ray +/// @param point the point of initial intersection +/// @param normal the normal vector at the point of intersection, zero for a shape cast with initial overlap +/// @param fraction the fraction along the ray at the point of intersection, zero for a shape cast with initial overlap +/// @param context the user context +/// @return -1 to filter, 0 to terminate, fraction to clip the ray for closest hit, 1 to continue +/// @see b2World_CastRay +/// @ingroup world +typedef float b2CastResultFcn( b2ShapeId shapeId, b2Vec2 point, b2Vec2 normal, float fraction, void* context ); + +// Used to collect collision planes for character movers. +// Return true to continue gathering planes. +typedef bool b2PlaneResultFcn( b2ShapeId shapeId, const b2PlaneResult* plane, void* context ); + +/// These colors are used for debug draw and mostly match the named SVG colors. +/// See https://www.rapidtables.com/web/color/index.html +/// https://johndecember.com/html/spec/colorsvg.html +/// https://upload.wikimedia.org/wikipedia/commons/2/2b/SVG_Recognized_color_keyword_names.svg +typedef enum b2HexColor +{ + b2_colorAliceBlue = 0xF0F8FF, + b2_colorAntiqueWhite = 0xFAEBD7, + b2_colorAqua = 0x00FFFF, + b2_colorAquamarine = 0x7FFFD4, + b2_colorAzure = 0xF0FFFF, + b2_colorBeige = 0xF5F5DC, + b2_colorBisque = 0xFFE4C4, + b2_colorBlack = 0x000000, + b2_colorBlanchedAlmond = 0xFFEBCD, + b2_colorBlue = 0x0000FF, + b2_colorBlueViolet = 0x8A2BE2, + b2_colorBrown = 0xA52A2A, + b2_colorBurlywood = 0xDEB887, + b2_colorCadetBlue = 0x5F9EA0, + b2_colorChartreuse = 0x7FFF00, + b2_colorChocolate = 0xD2691E, + b2_colorCoral = 0xFF7F50, + b2_colorCornflowerBlue = 0x6495ED, + b2_colorCornsilk = 0xFFF8DC, + b2_colorCrimson = 0xDC143C, + b2_colorCyan = 0x00FFFF, + b2_colorDarkBlue = 0x00008B, + b2_colorDarkCyan = 0x008B8B, + b2_colorDarkGoldenRod = 0xB8860B, + b2_colorDarkGray = 0xA9A9A9, + b2_colorDarkGreen = 0x006400, + b2_colorDarkKhaki = 0xBDB76B, + b2_colorDarkMagenta = 0x8B008B, + b2_colorDarkOliveGreen = 0x556B2F, + b2_colorDarkOrange = 0xFF8C00, + b2_colorDarkOrchid = 0x9932CC, + b2_colorDarkRed = 0x8B0000, + b2_colorDarkSalmon = 0xE9967A, + b2_colorDarkSeaGreen = 0x8FBC8F, + b2_colorDarkSlateBlue = 0x483D8B, + b2_colorDarkSlateGray = 0x2F4F4F, + b2_colorDarkTurquoise = 0x00CED1, + b2_colorDarkViolet = 0x9400D3, + b2_colorDeepPink = 0xFF1493, + b2_colorDeepSkyBlue = 0x00BFFF, + b2_colorDimGray = 0x696969, + b2_colorDodgerBlue = 0x1E90FF, + b2_colorFireBrick = 0xB22222, + b2_colorFloralWhite = 0xFFFAF0, + b2_colorForestGreen = 0x228B22, + b2_colorFuchsia = 0xFF00FF, + b2_colorGainsboro = 0xDCDCDC, + b2_colorGhostWhite = 0xF8F8FF, + b2_colorGold = 0xFFD700, + b2_colorGoldenRod = 0xDAA520, + b2_colorGray = 0x808080, + b2_colorGreen = 0x008000, + b2_colorGreenYellow = 0xADFF2F, + b2_colorHoneyDew = 0xF0FFF0, + b2_colorHotPink = 0xFF69B4, + b2_colorIndianRed = 0xCD5C5C, + b2_colorIndigo = 0x4B0082, + b2_colorIvory = 0xFFFFF0, + b2_colorKhaki = 0xF0E68C, + b2_colorLavender = 0xE6E6FA, + b2_colorLavenderBlush = 0xFFF0F5, + b2_colorLawnGreen = 0x7CFC00, + b2_colorLemonChiffon = 0xFFFACD, + b2_colorLightBlue = 0xADD8E6, + b2_colorLightCoral = 0xF08080, + b2_colorLightCyan = 0xE0FFFF, + b2_colorLightGoldenRodYellow = 0xFAFAD2, + b2_colorLightGray = 0xD3D3D3, + b2_colorLightGreen = 0x90EE90, + b2_colorLightPink = 0xFFB6C1, + b2_colorLightSalmon = 0xFFA07A, + b2_colorLightSeaGreen = 0x20B2AA, + b2_colorLightSkyBlue = 0x87CEFA, + b2_colorLightSlateGray = 0x778899, + b2_colorLightSteelBlue = 0xB0C4DE, + b2_colorLightYellow = 0xFFFFE0, + b2_colorLime = 0x00FF00, + b2_colorLimeGreen = 0x32CD32, + b2_colorLinen = 0xFAF0E6, + b2_colorMagenta = 0xFF00FF, + b2_colorMaroon = 0x800000, + b2_colorMediumAquaMarine = 0x66CDAA, + b2_colorMediumBlue = 0x0000CD, + b2_colorMediumOrchid = 0xBA55D3, + b2_colorMediumPurple = 0x9370DB, + b2_colorMediumSeaGreen = 0x3CB371, + b2_colorMediumSlateBlue = 0x7B68EE, + b2_colorMediumSpringGreen = 0x00FA9A, + b2_colorMediumTurquoise = 0x48D1CC, + b2_colorMediumVioletRed = 0xC71585, + b2_colorMidnightBlue = 0x191970, + b2_colorMintCream = 0xF5FFFA, + b2_colorMistyRose = 0xFFE4E1, + b2_colorMoccasin = 0xFFE4B5, + b2_colorNavajoWhite = 0xFFDEAD, + b2_colorNavy = 0x000080, + b2_colorOldLace = 0xFDF5E6, + b2_colorOlive = 0x808000, + b2_colorOliveDrab = 0x6B8E23, + b2_colorOrange = 0xFFA500, + b2_colorOrangeRed = 0xFF4500, + b2_colorOrchid = 0xDA70D6, + b2_colorPaleGoldenRod = 0xEEE8AA, + b2_colorPaleGreen = 0x98FB98, + b2_colorPaleTurquoise = 0xAFEEEE, + b2_colorPaleVioletRed = 0xDB7093, + b2_colorPapayaWhip = 0xFFEFD5, + b2_colorPeachPuff = 0xFFDAB9, + b2_colorPeru = 0xCD853F, + b2_colorPink = 0xFFC0CB, + b2_colorPlum = 0xDDA0DD, + b2_colorPowderBlue = 0xB0E0E6, + b2_colorPurple = 0x800080, + b2_colorRebeccaPurple = 0x663399, + b2_colorRed = 0xFF0000, + b2_colorRosyBrown = 0xBC8F8F, + b2_colorRoyalBlue = 0x4169E1, + b2_colorSaddleBrown = 0x8B4513, + b2_colorSalmon = 0xFA8072, + b2_colorSandyBrown = 0xF4A460, + b2_colorSeaGreen = 0x2E8B57, + b2_colorSeaShell = 0xFFF5EE, + b2_colorSienna = 0xA0522D, + b2_colorSilver = 0xC0C0C0, + b2_colorSkyBlue = 0x87CEEB, + b2_colorSlateBlue = 0x6A5ACD, + b2_colorSlateGray = 0x708090, + b2_colorSnow = 0xFFFAFA, + b2_colorSpringGreen = 0x00FF7F, + b2_colorSteelBlue = 0x4682B4, + b2_colorTan = 0xD2B48C, + b2_colorTeal = 0x008080, + b2_colorThistle = 0xD8BFD8, + b2_colorTomato = 0xFF6347, + b2_colorTurquoise = 0x40E0D0, + b2_colorViolet = 0xEE82EE, + b2_colorWheat = 0xF5DEB3, + b2_colorWhite = 0xFFFFFF, + b2_colorWhiteSmoke = 0xF5F5F5, + b2_colorYellow = 0xFFFF00, + b2_colorYellowGreen = 0x9ACD32, + + b2_colorBox2DRed = 0xDC3132, + b2_colorBox2DBlue = 0x30AEBF, + b2_colorBox2DGreen = 0x8CC924, + b2_colorBox2DYellow = 0xFFEE8C +} b2HexColor; + +/// This struct holds callbacks you can implement to draw a Box2D world. +/// This structure should be zero initialized. +/// @ingroup world +typedef struct b2DebugDraw +{ + /// Draw a closed polygon provided in CCW order. + void ( *DrawPolygonFcn )( const b2Vec2* vertices, int vertexCount, b2HexColor color, void* context ); + + /// Draw a solid closed polygon provided in CCW order. + void ( *DrawSolidPolygonFcn )( b2Transform transform, const b2Vec2* vertices, int vertexCount, float radius, b2HexColor color, + void* context ); + + /// Draw a circle. + void ( *DrawCircleFcn )( b2Vec2 center, float radius, b2HexColor color, void* context ); + + /// Draw a solid circle. + void ( *DrawSolidCircleFcn )( b2Transform transform, float radius, b2HexColor color, void* context ); + + /// Draw a solid capsule. + void ( *DrawSolidCapsuleFcn )( b2Vec2 p1, b2Vec2 p2, float radius, b2HexColor color, void* context ); + + /// Draw a line segment. + void ( *DrawLineFcn )( b2Vec2 p1, b2Vec2 p2, b2HexColor color, void* context ); + + /// Draw a transform. Choose your own length scale. + void ( *DrawTransformFcn )( b2Transform transform, void* context ); + + /// Draw a point. + void ( *DrawPointFcn )( b2Vec2 p, float size, b2HexColor color, void* context ); + + /// Draw a string in world space + void ( *DrawStringFcn )( b2Vec2 p, const char* s, b2HexColor color, void* context ); + + /// World bounds to use for debug draw + b2AABB drawingBounds; + + /// Scale to use when drawing forces + float forceScale; + + /// Global scaling for joint drawing + float jointScale; + + /// Option to draw shapes + bool drawShapes; + + /// Option to draw joints + bool drawJoints; + + /// Option to draw additional information for joints + bool drawJointExtras; + + /// Option to draw the bounding boxes for shapes + bool drawBounds; + + /// Option to draw the mass and center of mass of dynamic bodies + bool drawMass; + + /// Option to draw body names + bool drawBodyNames; + + /// Option to draw contact points + bool drawContactPoints; + + /// Option to visualize the graph coloring used for contacts and joints + bool drawGraphColors; + + /// Option to draw contact feature ids + bool drawContactFeatures; + + /// Option to draw contact normals + bool drawContactNormals; + + /// Option to draw contact normal forces + bool drawContactForces; + + /// Option to draw contact friction forces + bool drawFrictionForces; + + /// Option to draw islands as bounding boxes + bool drawIslands; + + /// User context that is passed as an argument to drawing callback functions + void* context; +} b2DebugDraw; + +/// Use this to initialize your drawing interface. This allows you to implement a sub-set +/// of the drawing functions. +B2_API b2DebugDraw b2DefaultDebugDraw( void ); diff --git a/libs/box2d/libbox2d.so.3.2.0 b/libs/box2d/libbox2d.so.3.2.0 new file mode 100755 index 0000000..e3645e9 --- /dev/null +++ b/libs/box2d/libbox2d.so.3.2.0 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aadc60bc0a1fafe79e1f14667641444c0a54d75f72d1fc92a522a77ca7c84577 +size 653688 diff --git a/libs/rgfw/RGFW.h b/libs/rgfw/RGFW.h new file mode 100644 index 0000000..e48d483 --- /dev/null +++ b/libs/rgfw/RGFW.h @@ -0,0 +1,14231 @@ +/* +* +* RGFW 1.9.0-dev + +* Copyright (C) 2022-26 Riley Mabb (@ColleagueRiley) +* +* libpng license +* +* 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. +* +* +*/ + +/* + (MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION) + #define RGFW_IMPLEMENTATION - makes it so source code is included with header +*/ + +/* + #define RGFW_IMPLEMENTATION - (required) makes it so the source code is included + #define RGFW_DEBUG - (optional) makes it so RGFW prints debug messages and errors when they're found + #define RGFW_EGL - (optional) compile with OpenGL functions, allowing you to use to use EGL instead of the native OpenGL functions + #define RGFW_DIRECTX - (optional) include integration directX functions (windows only) + #define RGFW_VULKAN - (optional) include helpful vulkan integration functions and macros + #define RGFW_WEBGPU - (optional) use WebGPU for rendering + #define RGFW_NATIVE - (optional) define native RGFW types that use native API structures + + #define RGFW_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS + #define RGFW_WAYLAND (optional) (unix only) use Wayland. (This can be used with X11) + #define RGFW_NO_X11 (optional) (unix only) don't fallback to X11 when using Wayland + #define RGFW_NO_LOAD_WGL (optional) (windows only) if WGL should be loaded dynamically during runtime + #define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor + #define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) use XCursor, but don't link it in code, (you'll have to link it with -lXcursor) + #define RGFW_NO_X11_EXT_PRELOAD (optional) (unix only) use Xext, but don't link it in code, (you'll have to link it with -lXext) + #define RGFW_NO_LOAD_WINMM (optional) (windows only) use winmm (timeBeginPeriod), but don't link it in code, (you'll have to link it with -lwinmm) + #define RGFW_NO_WINMM (optional) (windows only) don't use winmm + #define RGFW_NO_IOKIT (optional) (macOS) don't use IOKit + #define RGFW_NO_UNIX_CLOCK (optional) (unix) don't link unix clock functions + #define RGFW_NO_DWM (windows only) - do not use or link dwmapi + #define RGFW_USE_XDL (optional) (X11) if XDL (XLib Dynamic Loader) should be used to load X11 dynamically during runtime (must include XDL.h along with RGFW) + #define RGFW_COCOA_GRAPHICS_SWITCHING - (optional) (cocoa) use automatic graphics switching (allow the system to choose to use GPU or iGPU) + #define RGFW_COCOA_FRAME_NAME (optional) (cocoa) set frame name + #define RGFW_NO_DPI - do not calculate DPI (no XRM nor libShcore included) + #define RGFW_ADVANCED_SMOOTH_RESIZE - use advanced methods for smooth resizing (may result in a spike in memory usage or worse performance) (eg. WM_TIMER and XSyncValue) + #define RGFW_NO_INFO - do not define the RGFW_info struct (without RGFW_IMPLEMENTATION) + #define RGFW_NO_GLXWINDOW - do not use GLXWindow + + #define RGFW_ALLOC x - choose the default allocation function (defaults to standard malloc) + #define RGFW_FREE x - choose the default deallocation function (defaults to standard free) + #define RGFW_USERPTR x - choose the default userptr sent to the malloc call, (NULL by default) + + #define RGFW_EXPORT - use when building RGFW + #define RGFW_IMPORT - use when linking with RGFW (not as a single-header) + + #define RGFW_USE_INT - force the use c-types rather than stdint.h (for systems that might not have stdint.h (msvc)) + #define RGFW_bool x - choose what type to use for bool, by default u32 is used +*/ + +/* +Example to get you started : + +linux : gcc main.c -lX11 -lXrandr -lGL +windows : gcc main.c -lopengl32 -lgdi32 +macos : gcc main.c -framework Cocoa -framework CoreVideo -framework OpenGL -framework IOKit + +#define RGFW_IMPLEMENTATION +#include "RGFW.h" + +u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF}; + +int main() { + RGFW_window* win = RGFW_createWindow("name", 100, 100, 500, 500, (u64)0); + RGFW_event event; + + RGFW_window_setExitKey(win, RGFW_escape); + RGFW_window_setIcon(win, icon, 3, 3, RGFW_formatRGBA8); + + while (RGFW_window_shouldClose(win) == RGFW_FALSE) { + while (RGFW_window_checkEvent(win, &event)) { + if (event.type == RGFW_quit) + break; + } + } + + RGFW_window_close(win); +} + + compiling : + + if you wish to compile the library all you have to do is create a new file with this in it + + rgfw.c + #define RGFW_IMPLEMENTATION + #include "RGFW.h" + + You may also want to add + `#define RGFW_EXPORT` when compiling and + `#define RGFW_IMPORT`when linking RGFW on it's own: + this reduces inline functions and prevents bloat in the object file + + then you can use gcc (or whatever compile you wish to use) to compile the library into object file + + ex. gcc -c RGFW.c -fPIC + + after you compile the library into an object file, you can also turn the object file into an static or shared library + + (commands ar and gcc can be replaced with whatever equivalent your system uses) + + static : ar rcs RGFW.a RGFW.o + shared : + windows: + gcc -shared RGFW.o -lopengl32 -lgdi32 -o RGFW.dll + linux: + gcc -shared RGFW.o -lX11 -lGL -lXrandr -o RGFW.so + macos: + gcc -shared RGFW.o -framework CoreVideo -framework Cocoa -framework OpenGL -framework IOKit +*/ + + + +/* + Credits : + EimaMei/Sacode : Code review, helped with X11, MacOS and Windows support, Silicon, siliapp.h -> referencing + + stb : This project is heavily inspired by the stb single header files + + SDL, GLFW and other online resources : reference implementations + + contributors : (feel free to put yourself here if you contribute) + krisvers (@krisvers) -> code review + EimaMei (@SaCode) -> code review + Nycticebus (@Code-Nycticebus) -> bug fixes + Rob Rohan (@robrohan) -> X11 bugs and missing features, MacOS/Cocoa fixing memory issues/bugs + AICDG (@THISISAGOODNAME) -> vulkan support (example) + @Easymode -> support, testing/debugging, bug fixes and reviews + Joshua Rowe (omnisci3nce) - bug fix, review (macOS) + @lesleyrs -> bug fix, review (OpenGL) + Nick Porcino (@meshula) - testing, organization, review (MacOS, examples) + @therealmarrakesh -> documentation + @DarekParodia -> code review (X11) (C++) + @NishiOwO -> fix BSD support, fix OSMesa example + @BaynariKattu -> code review and documentation + Miguel Pinto (@konopimi) -> code review, fix vulkan example + @m-doescode -> code review (wayland) + Robert Gonzalez (@uni-dos) -> code review (wayland) + @TheLastVoyager -> code review + @yehoravramenko -> code review (winapi) + @halocupcake -> code review (OpenGL) + @GideonSerf -> documentation + Alexandre Almeida (@M374LX) -> code review (keycodes) + Vũ Xuân Trường (@wanwanvxt) -> code review (winapi) + Lucas (@lightspeedlucas) -> code review (msvc++) + Jeffery Myers (@JeffM2501) -> code review (msvc) + Zeni (@zenitsuyo) -> documentation + TheYahton (@TheYahton) -> documentation + nonexistant_object (@DiarrheaMcgee) + AC Gaudette (@acgaudette) +*/ + +#if _MSC_VER + #pragma comment(lib, "gdi32") + #pragma comment(lib, "shell32") + #pragma comment(lib, "User32") + #pragma comment(lib, "Advapi32") + #pragma warning( push ) + #pragma warning( disable : 4996 4191 4127) + #if _MSC_VER < 600 + #define RGFW_C89 + #endif +#else + #if defined(__STDC__) && !defined(__STDC_VERSION__) + #define RGFW_C89 + #endif +#endif + +#if defined(RGFW_EGL) && !defined(RGFW_OPENGL) + #define RGFW_OPENGL +#endif + +/* these OS macros look better & are standardized */ +/* plus it helps with cross-compiling */ + +#ifdef __EMSCRIPTEN__ + #define RGFW_WASM +#endif + +#if defined(RGFW_X11) && defined(__APPLE__) && !defined(RGFW_CUSTOM_BACKEND) + #define RGFW_MACOS_X11 + #define RGFW_UNIX +#endif + +#if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_UNIX) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) /* (if you're using X11 on windows some how) */ + #define RGFW_WINDOWS +#endif +#if defined(RGFW_WAYLAND) + #define RGFW_DEBUG /* wayland will be in debug mode by default for now */ + #define RGFW_UNIX + #ifdef RGFW_OPENGL + #define RGFW_EGL + #endif + #ifdef RGFW_X11 + #define RGFW_DYNAMIC + #endif +#endif +#if (!defined(RGFW_WAYLAND) && !defined(RGFW_X11)) && (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11)) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) + #define RGFW_MACOS_X11 + #define RGFW_X11 + #define RGFW_UNIX +#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) + #define RGFW_MACOS +#endif + +#ifndef RGFW_ASSERT + #include + #define RGFW_ASSERT assert +#endif + +#if !defined(__STDC_VERSION__) + #define RGFW_C89 +#endif + +#if !defined(RGFW_SNPRINTF) && (defined(RGFW_X11) || defined(RGFW_WAYLAND)) + + /* required for X11 errors */ + #include + + #ifdef RGFW_C89 + #include + static int RGFW_c89_snprintf(char *dst, size_t size, const char *format, ...) { + va_list args; + size_t count = 0; + va_start(args, format); + count = (size_t)vsprintf(dst, format, args); + RGFW_ASSERT(count + 1 < size && "Buffer overflow"); + va_end(args); + return (int)count; + } + #define RGFW_SNPRINTF RGFW_c89_snprintf + #else + #define RGFW_SNPRINTF snprintf + #endif /*RGFW_C89*/ +#endif + +#ifndef RGFW_USERPTR + #define RGFW_USERPTR NULL +#endif + +#ifndef RGFW_UNUSED + #define RGFW_UNUSED(x) (void)(x) +#endif + +#ifndef RGFW_ROUND + #define RGFW_ROUND(x) (i32)((x) >= 0 ? (x) + 0.5f : (x) - 0.5f) +#endif + +#ifndef RGFW_MIN + #define RGFW_MIN(x, y) ((x < y) ? x : y) +#endif + +#ifndef RGFW_ALLOC + #include + #define RGFW_ALLOC malloc + #define RGFW_FREE free +#endif + +#if !defined(RGFW_MEMCPY) || !defined(RGFW_STRNCMP) || !defined(RGFW_STRNCPY) || !defined(RGFW_MEMSET) + #include +#endif + +#ifndef RGFW_MEMSET + #define RGFW_MEMSET(ptr, value, num) memset(ptr, value, num) +#endif + +#ifndef RGFW_MEMCPY + #define RGFW_MEMCPY(dist, src, len) memcpy(dist, src, len) +#endif + +#ifndef RGFW_STRNCMP + #define RGFW_STRNCMP(s1, s2, max) strncmp(s1, s2, max) +#endif + +#ifndef RGFW_STRNCPY + #define RGFW_STRNCPY(dist, src, len) strncpy(dist, src, len) +#endif + +#ifndef RGFW_STRSTR + #define RGFW_STRSTR(str, substr) strstr(str, substr) +#endif + +#ifndef RGFW_STRTOL + /* required for X11 XDnD and X11 Monitor DPI */ + #include + #define RGFW_STRTOL(str, endptr, base) strtol(str, endptr, base) + #define RGFW_ATOF(num) atof(num) +#endif + +#if !defined(RGFW_PRINTF) && ( defined(RGFW_DEBUG) || defined(RGFW_WAYLAND) ) + /* required when using RGFW_DEBUG */ + #include + #define RGFW_PRINTF printf +#endif + +#ifndef RGFW_MAX_PATH + #define RGFW_MAX_PATH 260 /* max length of a path (for drag andn drop) */ +#endif +#ifndef RGFW_MAX_DROPS + #define RGFW_MAX_DROPS 260 /* max items you can drop at once */ +#endif + +#ifndef RGFW_MAX_EVENTS + #define RGFW_MAX_EVENTS 32 +#endif + +#ifndef RGFW_MAX_MONITORS + #define RGFW_MAX_MONITORS 6 +#endif + +#ifndef RGFW_COCOA_FRAME_NAME + #define RGFW_COCOA_FRAME_NAME NULL +#endif + +#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */ + #define RGFW_NO_MONITOR + #define RGFW_NO_PASSTHROUGH +#endif + +#if defined(RGFW_EXPORT) || defined(RGFW_IMPORT) + #if defined(_WIN32) + #if defined(__TINYC__) && (defined(RGFW_EXPORT) || defined(RGFW_IMPORT)) + #define __declspec(x) __attribute__((x)) + #endif + + #if defined(RGFW_EXPORT) + #define RGFWDEF __declspec(dllexport) + #else + #define RGFWDEF __declspec(dllimport) + #endif + #else + #if defined(RGFW_EXPORT) + #define RGFWDEF __attribute__((visibility("default"))) + #endif + #endif + #ifndef RGFWDEF + #define RGFWDEF + #endif +#endif + +#ifndef RGFWDEF + #ifdef RGFW_C89 + #define RGFWDEF __inline + #else + #define RGFWDEF inline + #endif +#endif + +#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) + extern "C" { +#endif + +/* makes sure the header file part is only defined once by default */ +#ifndef RGFW_HEADER + +#define RGFW_HEADER + +#include +#ifndef RGFW_INT_DEFINED + #ifdef RGFW_USE_INT /* optional for any system that might not have stdint.h */ + typedef unsigned char u8; + typedef signed char i8; + typedef unsigned short u16; + typedef signed short i16; + typedef unsigned long int u32; + typedef signed long int i32; + typedef unsigned long long u64; + typedef signed long long i64; + #else /* use stdint standard types instead of c "standard" types */ + #include + + typedef uint8_t u8; + typedef int8_t i8; + typedef uint16_t u16; + typedef int16_t i16; + typedef uint32_t u32; + typedef int32_t i32; + typedef uint64_t u64; + typedef int64_t i64; + #endif + #define RGFW_INT_DEFINED +#endif + +typedef ptrdiff_t RGFW_ssize_t; + +#ifndef RGFW_BOOL_DEFINED + #define RGFW_BOOL_DEFINED + typedef u8 RGFW_bool; +#endif + +#define RGFW_BOOL(x) (RGFW_bool)((x) != 0) /* force a value to be 0 or 1 */ +#define RGFW_TRUE (RGFW_bool)1 +#define RGFW_FALSE (RGFW_bool)0 + +#define RGFW_ENUM(type, name) type name; enum +#define RGFW_BIT(x) (1 << (x)) + +#ifdef RGFW_VULKAN + + #if defined(RGFW_WAYLAND) && defined(RGFW_X11) + #define VK_USE_PLATFORM_WAYLAND_KHR + #define VK_USE_PLATFORM_XLIB_KHR + #define RGFW_VK_SURFACE ((RGFW_usingWayland()) ? ("VK_KHR_wayland_surface") : ("VK_KHR_xlib_surface")) + #elif defined(RGFW_WAYLAND) + #define VK_USE_PLATFORM_WAYLAND_KHR + #define VK_USE_PLATFORM_XLIB_KHR + #define RGFW_VK_SURFACE "VK_KHR_wayland_surface" + #elif defined(RGFW_X11) + #define VK_USE_PLATFORM_XLIB_KHR + #define RGFW_VK_SURFACE "VK_KHR_xlib_surface" + #elif defined(RGFW_WINDOWS) + #define VK_USE_PLATFORM_WIN32_KHR + #define OEMRESOURCE + #define RGFW_VK_SURFACE "VK_KHR_win32_surface" + #elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) + #define VK_USE_PLATFORM_MACOS_MVK + #define RGFW_VK_SURFACE "VK_MVK_macos_surface" + #else + #define RGFW_VK_SURFACE NULL + #endif + +#endif + + +/*! @brief The stucture that contains information about the current RGFW instance */ +typedef struct RGFW_info RGFW_info; + +/*! @brief The window stucture for interfacing with the window */ +typedef struct RGFW_window RGFW_window; + +/*! @brief The source window stucture for interfacing with the underlying windowing API (e.g. winapi, wayland, cocoa, etc) */ +typedef struct RGFW_window_src RGFW_window_src; + +/*! @brief The color format for pixel data */ +typedef RGFW_ENUM(u8, RGFW_format) { + RGFW_formatRGB8 = 0, /*!< 8-bit RGB (3 channels) */ + RGFW_formatBGR8, /*!< 8-bit BGR (3 channels) */ + RGFW_formatRGBA8, /*!< 8-bit RGBA (4 channels) */ + RGFW_formatARGB8, /*!< 8-bit RGBA (4 channels) */ + RGFW_formatBGRA8, /*!< 8-bit BGRA (4 channels) */ + RGFW_formatABGR8, /*!< 8-bit BGRA (4 channels) */ + RGFW_formatCount +}; + +/*! @brief a stucture for interfacing with the underlying native image (e.g. XImage, HBITMAP, etc) */ +typedef struct RGFW_nativeImage RGFW_nativeImage; + +/*! @brief a stucture for interfacing with pixel data as a renderable surface */ +typedef struct RGFW_surface RGFW_surface; + +/*! a raw pointer to the underlying mouse handle for setting and creating custom mouse icons */ +typedef void RGFW_mouse; + +/*! @brief RGFW's abstract keycodes */ +typedef RGFW_ENUM(u8, RGFW_key) { + RGFW_keyNULL = 0, + RGFW_escape = '\033', + RGFW_backtick = '`', + RGFW_0 = '0', + RGFW_1 = '1', + RGFW_2 = '2', + RGFW_3 = '3', + RGFW_4 = '4', + RGFW_5 = '5', + RGFW_6 = '6', + RGFW_7 = '7', + RGFW_8 = '8', + RGFW_9 = '9', + RGFW_minus = '-', + RGFW_equal = '=', + RGFW_equals = RGFW_equal, + RGFW_backSpace = '\b', + RGFW_tab = '\t', + RGFW_space = ' ', + RGFW_a = 'a', + RGFW_b = 'b', + RGFW_c = 'c', + RGFW_d = 'd', + RGFW_e = 'e', + RGFW_f = 'f', + RGFW_g = 'g', + RGFW_h = 'h', + RGFW_i = 'i', + RGFW_j = 'j', + RGFW_k = 'k', + RGFW_l = 'l', + RGFW_m = 'm', + RGFW_n = 'n', + RGFW_o = 'o', + RGFW_p = 'p', + RGFW_q = 'q', + RGFW_r = 'r', + RGFW_s = 's', + RGFW_t = 't', + RGFW_u = 'u', + RGFW_v = 'v', + RGFW_w = 'w', + RGFW_x = 'x', + RGFW_y = 'y', + RGFW_z = 'z', + RGFW_period = '.', + RGFW_comma = ',', + RGFW_slash = '/', + RGFW_bracket = '[', + RGFW_closeBracket = ']', + RGFW_semicolon = ';', + RGFW_apostrophe = '\'', + RGFW_backSlash = '\\', + RGFW_return = '\n', + RGFW_enter = RGFW_return, + RGFW_delete = '\177', /* 127 */ + RGFW_F1, + RGFW_F2, + RGFW_F3, + RGFW_F4, + RGFW_F5, + RGFW_F6, + RGFW_F7, + RGFW_F8, + RGFW_F9, + RGFW_F10, + RGFW_F11, + RGFW_F12, + RGFW_F13, + RGFW_F14, + RGFW_F15, + RGFW_F16, + RGFW_F17, + RGFW_F18, + RGFW_F19, + RGFW_F20, + RGFW_F21, + RGFW_F22, + RGFW_F23, + RGFW_F24, + RGFW_F25, + RGFW_capsLock, + RGFW_shiftL, + RGFW_controlL, + RGFW_altL, + RGFW_superL, + RGFW_shiftR, + RGFW_controlR, + RGFW_altR, + RGFW_superR, + RGFW_up, + RGFW_down, + RGFW_left, + RGFW_right, + RGFW_insert, + RGFW_menu, + RGFW_end, + RGFW_home, + RGFW_pageUp, + RGFW_pageDown, + RGFW_numLock, + RGFW_kpSlash, + RGFW_kpMultiply, + RGFW_kpPlus, + RGFW_kpMinus, + RGFW_kpEqual, + RGFW_kpEquals = RGFW_kpEqual, + RGFW_kp1, + RGFW_kp2, + RGFW_kp3, + RGFW_kp4, + RGFW_kp5, + RGFW_kp6, + RGFW_kp7, + RGFW_kp8, + RGFW_kp9, + RGFW_kp0, + RGFW_kpPeriod, + RGFW_kpReturn, + RGFW_scrollLock, + RGFW_printScreen, + RGFW_pause, + RGFW_world1, + RGFW_world2, + RGFW_keyLast = 256 /* padding for alignment ~(175 by default) */ +}; + +/*! @brief abstract mouse button codes */ +typedef RGFW_ENUM(u8, RGFW_mouseButton) { + RGFW_mouseLeft = 0, /*!< left mouse button is pressed */ + RGFW_mouseMiddle, /*!< mouse-wheel-button is pressed */ + RGFW_mouseRight, /*!< right mouse button is pressed */ + RGFW_mouseMisc1, RGFW_mouseMisc2, RGFW_mouseMisc3, RGFW_mouseMisc4, RGFW_mouseMisc5, + RGFW_mouseFinal +}; + +/*! abstract key modifier codes */ +typedef RGFW_ENUM(u8, RGFW_keymod) { + RGFW_modCapsLock = RGFW_BIT(0), + RGFW_modNumLock = RGFW_BIT(1), + RGFW_modControl = RGFW_BIT(2), + RGFW_modAlt = RGFW_BIT(3), + RGFW_modShift = RGFW_BIT(4), + RGFW_modSuper = RGFW_BIT(5), + RGFW_modScrollLock = RGFW_BIT(6) +}; + +/*! @brief codes for the event types that can be sent */ +typedef RGFW_ENUM(u8, RGFW_eventType) { + RGFW_eventNone = 0, /*!< no event has been sent */ + RGFW_keyPressed, /*!< a key has been pressed */ + RGFW_keyReleased, /*!< a key has been released */ + /*! key event note + the code of the key pressed is stored in + RGFW_event.key.value + !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! + + while a string version is stored in + RGFW_event.key.valueString + + RGFW_event.key.mod holds the current mod + this means if CapsLock, NumLock are active or not + */ + RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right) */ + RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right) */ + RGFW_mouseScroll, /*!< a mouse scroll event */ + RGFW_mousePosChanged, /*!< the position of the mouse has been changed */ + /*! mouse event note + the x and y of the mouse can be found in the vector, RGFW_x, y + + RGFW_event.button.value holds which mouse button was pressed + */ + RGFW_windowMoved, /*!< the window was moved (by the user) */ + RGFW_windowResized, /*!< the window was resized (by the user), [on WASM this means the browser was resized] */ + RGFW_focusIn, /*!< window is in focus now */ + RGFW_focusOut, /*!< window is out of focus now */ + RGFW_mouseEnter, /* mouse entered the window */ + RGFW_mouseLeave, /* mouse left the window */ + RGFW_windowRefresh, /* The window content needs to be refreshed */ + + /* attribs change event note + The event data is sent straight to the window structure + with win->x, win->y, win->w and win->h + */ + RGFW_quit, /*!< the user clicked the quit button */ + RGFW_dataDrop, /*!< a file has been dropped into the window */ + RGFW_dataDrag, /*!< the start of a drag and drop event, when the file is being dragged */ + /* drop data note + The x and y coords of the drop are stored in the vector RGFW_x, y + + RGFW_event.drop.count holds how many files were dropped + + This is also the size of the array which stores all the dropped file string, + RGFW_event.drop.files + */ + RGFW_windowMaximized, /*!< the window was maximized */ + RGFW_windowMinimized, /*!< the window was minimized */ + RGFW_windowRestored, /*!< the window was restored */ + RGFW_scaleUpdated /*!< content scale factor changed */ +}; + +/*! @brief flags for toggling wether or not an event should be processed */ +typedef RGFW_ENUM(u32, RGFW_eventFlag) { + RGFW_keyPressedFlag = RGFW_BIT(RGFW_keyPressed), + RGFW_keyReleasedFlag = RGFW_BIT(RGFW_keyReleased), + RGFW_mouseScrollFlag = RGFW_BIT(RGFW_mouseScroll), + RGFW_mouseButtonPressedFlag = RGFW_BIT(RGFW_mouseButtonPressed), + RGFW_mouseButtonReleasedFlag = RGFW_BIT(RGFW_mouseButtonReleased), + RGFW_mousePosChangedFlag = RGFW_BIT(RGFW_mousePosChanged), + RGFW_mouseEnterFlag = RGFW_BIT(RGFW_mouseEnter), + RGFW_mouseLeaveFlag = RGFW_BIT(RGFW_mouseLeave), + RGFW_windowMovedFlag = RGFW_BIT(RGFW_windowMoved), + RGFW_windowResizedFlag = RGFW_BIT(RGFW_windowResized), + RGFW_focusInFlag = RGFW_BIT(RGFW_focusIn), + RGFW_focusOutFlag = RGFW_BIT(RGFW_focusOut), + RGFW_windowRefreshFlag = RGFW_BIT(RGFW_windowRefresh), + RGFW_windowMaximizedFlag = RGFW_BIT(RGFW_windowMaximized), + RGFW_windowMinimizedFlag = RGFW_BIT(RGFW_windowMinimized), + RGFW_windowRestoredFlag = RGFW_BIT(RGFW_windowRestored), + RGFW_scaleUpdatedFlag = RGFW_BIT(RGFW_scaleUpdated), + RGFW_quitFlag = RGFW_BIT(RGFW_quit), + RGFW_dataDropFlag = RGFW_BIT(RGFW_dataDrop), + RGFW_dataDragFlag = RGFW_BIT(RGFW_dataDrag), + + RGFW_keyEventsFlag = RGFW_keyPressedFlag | RGFW_keyReleasedFlag, + RGFW_mouseEventsFlag = RGFW_mouseButtonPressedFlag | RGFW_mouseButtonReleasedFlag | RGFW_mousePosChangedFlag | RGFW_mouseEnterFlag | RGFW_mouseLeaveFlag | RGFW_mouseScrollFlag , + RGFW_windowEventsFlag = RGFW_windowMovedFlag | RGFW_windowResizedFlag | RGFW_windowRefreshFlag | RGFW_windowMaximizedFlag | RGFW_windowMinimizedFlag | RGFW_windowRestoredFlag | RGFW_scaleUpdatedFlag, + RGFW_focusEventsFlag = RGFW_focusInFlag | RGFW_focusOutFlag, + RGFW_dataDropEventsFlag = RGFW_dataDropFlag | RGFW_dataDragFlag, + RGFW_allEventFlags = RGFW_keyEventsFlag | RGFW_mouseEventsFlag | RGFW_windowEventsFlag | RGFW_focusEventsFlag | RGFW_dataDropEventsFlag | RGFW_quitFlag +}; + +/*! Event structure(s) and union for checking/getting events */ + +/*! @brief common event data across all events */ +typedef struct RGFW_commonEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies too (for event queue events) */ +} RGFW_commonEvent; + +/*! @brief event data for any mouse button event (press/release) */ +typedef struct RGFW_mouseButtonEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies too (for event queue events) */ + RGFW_mouseButton value; /* !< which mouse button was pressed */ +} RGFW_mouseButtonEvent; + +/*! @brief event data for any mouse scroll event */ +typedef struct RGFW_mouseScrollEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies too (for event queue events) */ + float x, y; /*!< the raw mouse scroll value */ +} RGFW_mouseScrollEvent; + +/*! @brief event data for any mouse position event (RGFW_mousePosChanged) */ +typedef struct RGFW_mousePosEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies too (for event queue events) */ + i32 x, y; /*!< mouse x, y of event (or drop point) */ + float vecX, vecY; /*!< raw mouse movement */ +} RGFW_mousePosEvent; + +/*! @brief event data for any key event (press/release) */ +typedef struct RGFW_keyEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies too (for event queue events) */ + RGFW_key value; /*!< the physical key of the event, refers to where key is physically !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */ + u8 sym; /*!< mapped key char of the event */ + RGFW_bool repeat; /*!< key press event repeated (the key is being held) */ + RGFW_keymod mod; +} RGFW_keyEvent; + +/*! @brief event data for any data drop event */ +typedef struct RGFW_dataDropEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies too (for event queue events) */ + /* 260 max paths with a max length of 260 */ + char** files; /*!< dropped files */ + size_t count; /*!< how many files were dropped */ +} RGFW_dataDropEvent; + +/*! @brief event data for any data drag event */ +typedef struct RGFW_dataDragEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies too (for event queue events) */ + i32 x, y; /*!< mouse x, y of event (or drop point) */ +} RGFW_dataDragEvent; + +/*! @brief event data for when the window scale (DPI) is updated */ +typedef struct RGFW_scaleUpdatedEvent { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_window* win; /*!< the window this event applies too (for event queue events) */ + float x, y; /*!< DPI scaling */ +} RGFW_scaleUpdatedEvent; + +/*! @brief union for all of the event stucture types */ +typedef union RGFW_event { + RGFW_eventType type; /*!< which event has been sent?*/ + RGFW_commonEvent common; /*!< common event data (e.g.) type and win */ + RGFW_mouseButtonEvent button; /*!< data for a button press/release */ + RGFW_mouseScrollEvent scroll; /*!< data for a mouse scroll */ + RGFW_mousePosEvent mouse; /*!< data for mouse motion events */ + RGFW_keyEvent key; /*!< data for key press/release/hold events */ + RGFW_dataDropEvent drop; /*!< dropping a file events */ + RGFW_dataDragEvent drag; /* data for dragging a file events */ + RGFW_scaleUpdatedEvent scale; /* data for monitor scaling events */ +} RGFW_event; + +/*! + @!brief codes for for RGFW_the code is stupid and C++ waitForEvent + waitMS -> Allows the function to keep checking for events even after there are no more events + if waitMS == 0, the loop will not wait for events + if waitMS > 0, the loop will wait that many miliseconds after there are no more events until it returns + if waitMS == -1 or waitMS == the max size of an unsigned 32-bit int, the loop will not return until it gets another event +*/ +typedef RGFW_ENUM(i32, RGFW_eventWait) { + RGFW_eventNoWait = 0, + RGFW_eventWaitNext = -1 +}; + + + +/*! @brief optional bitwise arguments for making a windows, these can be OR'd together */ +typedef RGFW_ENUM(u32, RGFW_windowFlags) { + RGFW_windowNoBorder = RGFW_BIT(0), /*!< the window doesn't have a border */ + RGFW_windowNoResize = RGFW_BIT(1), /*!< the window cannot be resized by the user */ + RGFW_windowAllowDND = RGFW_BIT(2), /*!< the window supports drag and drop */ + RGFW_windowHideMouse = RGFW_BIT(3), /*! the window should hide the mouse (can be toggled later on using `RGFW_window_showMouse`) */ + RGFW_windowFullscreen = RGFW_BIT(4), /*!< the window is fullscreen by default */ + RGFW_windowTransparent = RGFW_BIT(5), /*!< the window is transparent (only properly works on X11 and MacOS, although it's meant for for windows) */ + RGFW_windowCenter = RGFW_BIT(6), /*! center the window on the screen */ + RGFW_windowRawMouse = RGFW_BIT(7), /*!< use raw mouse mouse on window creation */ + RGFW_windowScaleToMonitor = RGFW_BIT(8), /*! scale the window to the screen */ + RGFW_windowHide = RGFW_BIT(9), /*! the window is hidden */ + RGFW_windowMaximize = RGFW_BIT(10), /*!< maximize the window on creation */ + RGFW_windowCenterCursor = RGFW_BIT(11), /*!< center the cursor to the window on creation */ + RGFW_windowFloating = RGFW_BIT(12), /*!< create a floating window */ + RGFW_windowFocusOnShow = RGFW_BIT(13), /*!< focus the window when it's shown */ + RGFW_windowMinimize = RGFW_BIT(14), /*!< focus the window when it's shown */ + RGFW_windowFocus = RGFW_BIT(15), /*!< if the window is in focus */ + RGFW_windowCaptureMouse = RGFW_BIT(16), /*!< capture the mouse mouse mouse on window creation */ + RGFW_windowOpenGL = RGFW_BIT(17), /*!< create an OpenGL context (you can also do this manually with RGFW_window_createContext_OpenGL) */ + RGFW_windowEGL = RGFW_BIT(18), /*!< create an EGL context (you can also do this manually with RGFW_window_createContext_EGL) */ + RGFW_windowedFullscreen = RGFW_windowNoBorder | RGFW_windowMaximize, + RGFW_windowCaptureRawMouse = RGFW_windowCaptureMouse | RGFW_windowRawMouse +}; + +/*! @brief the types of icon to set */ +typedef RGFW_ENUM(u8, RGFW_icon) { + RGFW_iconTaskbar = RGFW_BIT(0), + RGFW_iconWindow = RGFW_BIT(1), + RGFW_iconBoth = RGFW_iconTaskbar | RGFW_iconWindow +}; + +/*! @brief standard mouse icons */ +typedef RGFW_ENUM(u8, RGFW_mouseIcons) { + RGFW_mouseNormal = 0, + RGFW_mouseArrow, + RGFW_mouseIbeam, + RGFW_mouseText = RGFW_mouseIbeam, + RGFW_mouseCrosshair, + RGFW_mousePointingHand, + RGFW_mouseResizeEW, + RGFW_mouseResizeNS, + RGFW_mouseResizeNWSE, + RGFW_mouseResizeNESW, + RGFW_mouseResizeNW, + RGFW_mouseResizeN, + RGFW_mouseResizeNE, + RGFW_mouseResizeE, + RGFW_mouseResizeSE, + RGFW_mouseResizeS, + RGFW_mouseResizeSW, + RGFW_mouseResizeW, + RGFW_mouseResizeAll, + RGFW_mouseNotAllowed, + RGFW_mouseWait, + RGFW_mouseProgress, + RGFW_mouseIconCount, + RGFW_mouseIconFinal = 16 /* padding for alignment */ +}; + +/*! @breif flash request type */ +typedef RGFW_ENUM(u8, RGFW_flashRequest) { + RGFW_flashCancel = 0, + RGFW_flashBriefly, + RGFW_flashUntilFocused +}; + +/*! @brief the type of debug message */ +typedef RGFW_ENUM(u8, RGFW_debugType) { + RGFW_typeError = 0, RGFW_typeWarning, RGFW_typeInfo +}; + +/*! @brief error codes for known failure types */ +typedef RGFW_ENUM(u8, RGFW_errorCode) { + RGFW_noError = 0, /*!< no error */ + RGFW_errOutOfMemory, + RGFW_errOpenGLContext, RGFW_errEGLContext, /*!< error with the OpenGL context */ + RGFW_errWayland, RGFW_errX11, + RGFW_errDirectXContext, + RGFW_errIOKit, + RGFW_errClipboard, + RGFW_errFailedFuncLoad, + RGFW_errBuffer, + RGFW_errMetal, + RGFW_errEventQueue, + RGFW_infoMonitor, RGFW_infoWindow, RGFW_infoBuffer, RGFW_infoGlobal, RGFW_infoOpenGL, + RGFW_warningWayland, RGFW_warningOpenGL +}; + +/*! @brief callback function type for debug messags */ +typedef void (* RGFW_debugfunc)(RGFW_debugType type, RGFW_errorCode err, const char* msg); + +/*! @brief RGFW_windowMoved, the window and its new rect value */ +typedef void (* RGFW_windowMovedfunc)(RGFW_window* win, i32 x, i32 y); +/*! @brief RGFW_windowResized, the window and its new rect value */ +typedef void (* RGFW_windowResizedfunc)(RGFW_window* win, i32 w, i32 h); +/*! @brief RGFW_windowRestored, the window and its new rect value */ +typedef void (* RGFW_windowRestoredfunc)(RGFW_window* win, i32 x, i32 y, i32 w, i32 h); +/*! @brief RGFW_windowMaximized, the window and its new rect value */ +typedef void (* RGFW_windowMaximizedfunc)(RGFW_window* win, i32 x, i32 y, i32 w, i32 h); +/*! @brief RGFW_windowMinimized, the window and its new rect value */ +typedef void (* RGFW_windowMinimizedfunc)(RGFW_window* win); +/*! @brief RGFW_quit, the window that was closed */ +typedef void (* RGFW_windowQuitfunc)(RGFW_window* win); +/*! @brief RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its in focus */ +typedef void (* RGFW_focusfunc)(RGFW_window* win, RGFW_bool inFocus); +/*! @brief RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */ +typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, i32 x, i32 y, RGFW_bool status); +/*! @brief RGFW_mousePosChanged, the window that the move happened on, and the new point of the mouse */ +typedef void (* RGFW_mousePosfunc)(RGFW_window* win, i32 x, i32 y, float vecX, float vecY); +/*! @brief RGFW_dataDrag, the window, the point of the drop on the windows */ +typedef void (* RGFW_dataDragfunc)(RGFW_window* win, i32 x, i32 y); +/*! @brief RGFW_windowRefresh, the window that needs to be refreshed */ +typedef void (* RGFW_windowRefreshfunc)(RGFW_window* win); +/*! @brief RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the mapped key, the physical key, the string version, the state of the mod keys, if it was a press (else it's a release) */ +typedef void (* RGFW_keyfunc)(RGFW_window* win, u8 key, u8 sym, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool pressed); +/*! @brief RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */ +typedef void (* RGFW_mouseButtonfunc)(RGFW_window* win, RGFW_mouseButton button, RGFW_bool pressed); +/*! @brief RGFW_mouseScroll, the window that got the event, the x scroll value, the y scroll value */ +typedef void (* RGFW_mouseScrollfunc)(RGFW_window* win, float x, float y); +/*! @brief RGFW_dataDrop the window that had the drop, the drop data and the number of files dropped */ +typedef void (* RGFW_dataDropfunc)(RGFW_window* win, char** files, size_t count); +/*! @brief RGFW_scaleUpdated, the window the event was sent to, content scaleX, content scaleY */ +typedef void (* RGFW_scaleUpdatedfunc)(RGFW_window* win, float scaleX, float scaleY); + +/*! @brief function pointer equivalent of void* */ +typedef void (*RGFW_proc)(void); + +#ifndef RGFW_NO_MONITOR + +/*! @brief monitor mode data | can be changed by the user (with functions)*/ +typedef struct RGFW_monitorMode { + i32 w, h; /*!< monitor workarea size */ + u32 refreshRate; /*!< monitor refresh rate */ + u8 red, blue, green; +} RGFW_monitorMode; + +/*! @brief structure for monitor data */ +typedef struct RGFW_monitor { + i32 x, y; /*!< x - y of the monitor workarea */ + char name[128]; /*!< monitor name */ + float scaleX, scaleY; /*!< monitor content scale */ + float pixelRatio; /*!< pixel ratio for monitor (1.0 for regular, 2.0 for hiDPI) */ + float physW, physH; /*!< monitor physical size in inches */ + RGFW_monitorMode mode; +} RGFW_monitor; + +/*! @brief what type of request you are making for the monitor */ +typedef RGFW_ENUM(u8, RGFW_modeRequest) { + RGFW_monitorScale = RGFW_BIT(0), /*!< scale the monitor size */ + RGFW_monitorRefresh = RGFW_BIT(1), /*!< change the refresh rate */ + RGFW_monitorRGB = RGFW_BIT(2), /*!< change the monitor RGB bits size */ + RGFW_monitorAll = RGFW_monitorScale | RGFW_monitorRefresh | RGFW_monitorRGB +}; + +#endif + +#if defined(RGFW_OPENGL) + +/*! @brief abstract structure for interfacing with the underlying OpenGL API */ +typedef struct RGFW_glContext RGFW_glContext; + +/*! @brief abstract structure for interfacing with the underlying EGL API */ +typedef struct RGFW_eglContext RGFW_eglContext; + +/*! values for the releaseBehavior hint */ +typedef RGFW_ENUM(i32, RGFW_glReleaseBehavior) { + RGFW_glReleaseFlush = 0, /*!< flush the pipeline will be flushed when the context is release */ + RGFW_glReleaseNone /*!< do nothing on release */ +}; + +/*! values for the profile hint */ +typedef RGFW_ENUM(i32, RGFW_glProfile) { + RGFW_glCore = 0, /*!< the core OpenGL version, e.g. just support for that version */ + RGFW_glForwardCompatibility, /*!< only compatibility for newer versions of OpenGL as well as the requested version */ + RGFW_glCompatibility, /*!< allow compatibility for older versions of OpenGL as well as the requested version */ + RGFW_glES /*!< use OpenGL ES */ +}; + +/*! values for the renderer hint */ +typedef RGFW_ENUM(i32, RGFW_glRenderer) { + RGFW_glAccelerated = 0, /*!< hardware accelerated (GPU) */ + RGFW_glSoftware /*!< software rendered (CPU) */ +}; + +/*! OpenGL initalization hints */ +typedef struct RGFW_glHints { + i32 stencil; /*!< set stencil buffer bit size (0 by default) */ + i32 samples; /*!< set number of sample buffers (0 by default) */ + i32 stereo; /*!< hint the context to use stereoscopic frame buffers for 3D (false by default) */ + i32 auxBuffers; /*!< number of aux buffers (0 by default) */ + i32 doubleBuffer; /*!< request double buffering (true by default) */ + i32 red, green, blue, alpha; /*!< set color bit sizes (all 8 by default) */ + i32 depth; /*!< set depth buffer bit size (24 by default) */ + i32 accumRed, accumGreen, accumBlue, accumAlpha; /*!< set accumulated RGBA bit sizes (all 0 by default) */ + RGFW_bool sRGB; /*!< request sRGA format (false by default) */ + RGFW_bool robustness; /*!< request a "robust" (as in memory-safe) context (false by default). For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_robustness.txt */ + RGFW_bool debug; /*!< request OpenGL debugging (false by default). */ + RGFW_bool noError; /*!< request no OpenGL errors (false by default). This causes OpenGL errors to be undefined behavior. For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_no_error.txt */ + RGFW_glReleaseBehavior releaseBehavior; /*!< hint how the OpenGL driver should behave when changing contexts (RGFW_glReleaseNone by default). For more information check the overview section: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_context_flush_control.txt */ + RGFW_glProfile profile; /*!< set OpenGL API profile (RGFW_glCore by default) */ + i32 major, minor; /*!< set the OpenGL API profile version (by default RGFW_glMajor is 1, RGFW_glMinor is 0) */ + RGFW_glContext* share; /*!< Share this OpenGL context with newly created OpenGL contexts; defaults to NULL. */ + RGFW_eglContext* shareEGL; /*!< Share this EGL context with newly created OpenGL contexts; defaults to NULL. */ + RGFW_glRenderer renderer; /*!< renderer to use e.g. accelerated or software defaults to accelerated */ +} RGFW_glHints; + +#endif + +/**! + * @brief Allocates memory using the allocator defined by RGFW_ALLOC at compile time. + * @param size The size (in bytes) of the memory block to allocate. + * @return A pointer to the allocated memory block. +*/ +RGFWDEF void* RGFW_alloc(size_t size); + +/**! + * @brief Frees memory using the deallocator defined by RGFW_FREE at compile time. + * @param ptr A pointer to the memory block to free. +*/ +RGFWDEF void RGFW_free(void* ptr); + +/**! + * @brief Returns the size (in bytes) of the RGFW_window structure. + * @return The size of the RGFW_window structure. +*/ +RGFWDEF size_t RGFW_sizeofWindow(void); + +/**! + * @brief Returns the size (in bytes) of the RGFW_window_src structure. + * @return The size of the RGFW_window_src structure. +*/ +RGFWDEF size_t RGFW_sizeofWindowSrc(void); + +/**! + * @brief (Unix) Toggles the use of Wayland. + * This is enabled by default when compiled with `RGFW_WAYLAND`. + * If not using `RGFW_WAYLAND`, Wayland functions are not exposed. + * This function can be used to force the use of XWayland. + * @param wayland A boolean value indicating whether to use Wayland (true) or not (false). +*/ +RGFWDEF void RGFW_useWayland(RGFW_bool wayland); + +/**! + * @brief Checks if Wayland is currently being used. + * @return RGFW_TRUE if using Wayland, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_usingWayland(void); + +/**! + * @brief Retrieves the current Cocoa layer (macOS only). + * @return A pointer to the Cocoa layer, or NULL if the platform is not in use. +*/ +RGFWDEF void* RGFW_getLayer_OSX(void); + +/**! + * @brief Retrieves the current X11 display connection. + * @return A pointer to the X11 display, or NULL if the platform is not in use. +*/ +RGFWDEF void* RGFW_getDisplay_X11(void); + +/**! + * @brief Retrieves the current Wayland display connection. + * @return A pointer to the Wayland display (`struct wl_display*`), or NULL if the platform is not in use. +*/ +RGFWDEF struct wl_display* RGFW_getDisplay_Wayland(void); + +/**! + * @brief Sets the class name for X11 and WinAPI windows. + * Windows with the same class name will be grouped by the window manager. + * By default, the class name matches the root window’s name. + * @param name The class name to assign. +*/ +RGFWDEF void RGFW_setClassName(const char* name); + +/**! + * @brief Sets the X11 instance name. + * By default, the window name will be used as the instance name. + * @param name The X11 instance name to set. +*/ +RGFWDEF void RGFW_setXInstName(const char* name); + +/**! + * @brief (macOS only) Changes the current working directory to the application’s resource folder. +*/ +RGFWDEF void RGFW_moveToMacOSResourceDir(void); + +/*! copy image to another image, respecting each image's format */ +RGFWDEF void RGFW_copyImageData(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, u8* src_data, RGFW_format src_format); + +/**! + * @brief Returns the size (in bytes) of the RGFW_nativeImage structure. + * @return The size of the RGFW_nativeImage structure. +*/ +RGFWDEF size_t RGFW_sizeofNativeImage(void); + +/**! + * @brief Returns the size (in bytes) of the RGFW_surface structure. + * @return The size of the RGFW_surface structure. +*/ +RGFWDEF size_t RGFW_sizeofSurface(void); + +/**! + * @brief Creates a new surface from raw pixel data. + * @param data A pointer to the pixel data buffer. + * @param w The width of the surface in pixels. + * @param h The height of the surface in pixels. + * @param format The pixel format of the data. + * @return A pointer to the newly created RGFW_surface. + * + * NOTE: when you create a surface using RGFW_createSurface / ptr, on X11 it uses the root window's visual + * this means it may fail to render on any other window if the visual does not match + * RGFW_window_createSurface and RGFW_window_createSurfacePtr exist only for X11 to address this issues + * Of course, you can also manually set the root window with RGFW_setRootWindow +*/ +RGFWDEF RGFW_surface* RGFW_createSurface(u8* data, i32 w, i32 h, RGFW_format format); + +/**! + * @brief Creates a surface using a pre-allocated RGFW_surface structure. + * @param data A pointer to the pixel data buffer. + * @param w The width of the surface in pixels. + * @param h The height of the surface in pixels. + * @param format The pixel format of the data. + * @param surface A pointer to a pre-allocated RGFW_surface structure. + * @return RGFW_TRUE if successful, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface); + +/**! + * @brief Retrieves the native image associated with a surface. + * @param surface A pointer to the RGFW_surface. + * @return A pointer to the native RGFW_nativeImage associated with the surface. +*/ +RGFWDEF RGFW_nativeImage* RGFW_surface_getNativeImage(RGFW_surface* surface); + +/**! + * @brief Frees the surface pointer and any buffers used for software rendering. + * @param surface A pointer to the RGFW_surface to free. +*/ +RGFWDEF void RGFW_surface_free(RGFW_surface* surface); + +/**! + * @brief Frees only the internal buffers used for software rendering, leaving the surface struct intact. + * @param surface A pointer to the RGFW_surface whose buffers should be freed. +*/ +RGFWDEF void RGFW_surface_freePtr(RGFW_surface* surface); + + +/**! + * @brief Loads a mouse icon from bitmap data (similar to RGFW_window_setIcon). + * @param data A pointer to the bitmap pixel data. + * @param w The width of the mouse icon in pixels. + * @param h The height of the mouse icon in pixels. + * @param format The pixel format of the data. + * @return A pointer to the newly loaded RGFW_mouse structure. + * + * @note The icon is not resized by default. +*/ +RGFWDEF RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format); + +/**! + * @brief Frees the data associated with an RGFW_mouse structure. + * @param mouse A pointer to the RGFW_mouse to free. +*/ +RGFWDEF void RGFW_freeMouse(RGFW_mouse* mouse); + +#ifndef RGFW_NO_MONITOR + +/**! + * @brief Retrieves an array of all available monitors. + * @param len [OUTPUT] A pointer to store the number of monitors found (maximum of RGFW_MAX_MONITORS [6 by default]). + * @return A pointer to an array of RGFW_monitor structures. +*/ +RGFWDEF RGFW_monitor* RGFW_getMonitors(size_t* len); + +/**! + * @brief Retrieves the primary monitor. + * @return The RGFW_monitor structure representing the primary monitor. +*/ +RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void); + +/**! + * @brief Requests a specific display mode for a monitor. + * @param mon The monitor to apply the mode change to. + * @param mode The desired RGFW_monitorMode. + * @param request The RGFW_modeRequest describing how to handle the mode change. + * @return RGFW_TRUE if the mode was successfully applied, otherwise RGFW_FALSE. +*/ +RGFWDEF RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request); + +/**! + * @brief Compares two monitor modes to check if they are equivalent. + * @param mon The first monitor mode. + * @param mon2 The second monitor mode. + * @param request The RGFW_modeRequest that defines the comparison parameters. + * @return RGFW_TRUE if both modes are equivalent, otherwise RGFW_FALSE. +*/ +RGFWDEF RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request); + +/**! + * @brief Scales a monitor’s mode to match a window’s size. + * @param mon The monitor to be scaled. + * @param win The window whose size should be used as a reference. + * @return RGFW_TRUE if the scaling was successful, otherwise RGFW_FALSE. +*/ +RGFWDEF RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, struct RGFW_window* win); + +#endif + + /**! + * @brief set (enable or disable) raw mouse mode globally + * @param the boolean state of raw mouse mode + * +*/ +RGFWDEF void RGFW_setRawMouseMode(RGFW_bool state); + +/**! +* @brief sleep until RGFW gets an event or the timer ends (defined by OS) +* @param waitMS how long to wait for the next event (in miliseconds) +*/ +RGFWDEF void RGFW_waitForEvent(i32 waitMS); + +/**! +* @brief Set if events should be queued or not (enabled by default if the event queue is checked) +* @param queue boolean value if RGFW should queue events or not +*/ +RGFWDEF void RGFW_setQueueEvents(RGFW_bool queue); + +/**! +* @brief check all the events until there are none left and updates window structure attributes +*/ +RGFWDEF void RGFW_pollEvents(void); + +/**! +* @brief check all the events until there are none left and updates window structure attributes +* queues events if the queue is checked and/or requested +*/ +RGFWDEF void RGFW_stopCheckEvents(void); + +/**! + * @brief polls and pops the next event + * @param event [OUTPUT] a pointer to store the retrieved event + * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise + * + * NOTE: Using this function without a loop may cause event lag. + * For multi-threaded systems, use RGFW_pollEvents combined with RGFW_checkQueuedEvent. + * + * Example: + * RGFW_event event; + * while (RGFW_checkEvent(win, &event)) { + * // handle event + * } +*/ +RGFWDEF RGFW_bool RGFW_checkEvent(RGFW_event* event); + +/**! + * @brief pops the first queued event + * @param event [OUTPUT] a pointer to store the retrieved event + * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_checkQueuedEvent(RGFW_event* event); + +/** * @defgroup Input +* @{ */ + +/**! + * @brief returns true if the key is pressed during the current frame + * @param key the key code of the key you want to check + * @return The boolean value if the key is pressed or not +*/ +RGFWDEF RGFW_bool RGFW_isKeyPressed(RGFW_key key); + +/**! + * @brief returns true if the key was released during the current frame + * @param key the key code of the key you want to check + * @return The boolean value if the key is released or not +*/ +RGFWDEF RGFW_bool RGFW_isKeyReleased(RGFW_key key); + +/**! + * @brief returns true if the key is down + * @param key the key code of the key you want to check + * @return The boolean value if the key is down or not +*/ +RGFWDEF RGFW_bool RGFW_isKeyDown(RGFW_key key); + +/**! + * @brief returns true if the mouse button is pressed during the current frame + * @param button the mouse button code of the button you want to check + * @return The boolean value if the button is pressed or not +*/ +RGFWDEF RGFW_bool RGFW_isMousePressed(RGFW_mouseButton button); + +/**! + * @brief returns true if the mouse button is released during the current frame + * @param button the mouse button code of the button you want to check + * @return The boolean value if the button is released or not +*/ +RGFWDEF RGFW_bool RGFW_isMouseReleased(RGFW_mouseButton button); + +/**! + * @brief returns true if the mouse button is down + * @param button the mouse button code of the button you want to check + * @return The boolean value if the button is down or not +*/ +RGFWDEF RGFW_bool RGFW_isMouseDown(RGFW_mouseButton button); + +/**! + * @brief outputs the current x, y position of the mouse + * @param X [OUTPUT] a pointer for the output X value + * @param Y [OUTPUT] a pointer for the output Y value +*/ +RGFWDEF void RGFW_getMouseScroll(float* x, float* y); + +/**! + * @brief outputs the current x, y movement vector of the mouse + * @param X [OUTPUT] a pointer for the output X vector value + * @param Y [OUTPUT] a pointer for the output Y vector value +*/ +RGFWDEF void RGFW_getMouseVector(float* x, float* y); +/** @} */ + +/**! + * @brief creates a new window + * @param name the requested title of the window + * @param x the requested x position of the window + * @param y the requested y position of the window + * @param w the requested width of the window + * @param h the requested height of the window + * @param flags extra arguments ((u32)0 means no flags used) + * @return A pointer to the newly created window structure + * + * NOTE: (windows) if the executable has an icon resource named RGFW_ICON, it will be set as the initial icon for the window +*/ +RGFWDEF RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags); + +/**! + * @brief creates a new window using a pre-allocated window structure + * @param name the requested title of the window + * @param x the requested x position of the window + * @param y the requested y position of the window + * @param w the requested width of the window + * @param h the requested height of the window + * @param flags extra arguments ((u32)0 means no flags used) + * @param win a pointer the pre-allocated window structure + * @return A pointer to the newly created window structure +*/ +RGFWDEF RGFW_window* RGFW_createWindowPtr(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags, RGFW_window* win); + +/**! + * @brief creates a new surface structure + * @param win the source window of the surface + * @param data a pointer to the raw data of the structure (you allocate this) + * @param w the width the data + * @param h the height of the data + * @return A pointer to the newly created surface structure + * + * NOTE: when you create a surface using RGFW_createSurface / ptr, on X11 it uses the root window's visual + * this means it may fail to render on any other window if the visual does not match + * RGFW_window_createSurface and RGFW_window_createSurfacePtr exist only for X11 to address this issues + * Of course, you can also manually set the root window with RGFW_setRootWindow + */ +RGFWDEF RGFW_surface* RGFW_window_createSurface(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format); + +/**! + * @brief creates a new surface structure using a pre-allocated surface structure + * @param win the source window of the surface + * @param data a pointer to the raw data of the structure (you allocate this) + * @param w the width the data + * @param h the height of the data + * @param a pointer to the pre-allocated surface structure + * @return a bool if the creation was successful or not +*/ +RGFWDEF RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface); + +/**! + * @brief blits a surface stucture to the window + * @param win a pointer the window to blit to + * @param surface a pointer to the surface +*/ +RGFWDEF void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface); + +/**! + * @brief gets the position of the window | with RGFW_window.x and window.y + * @param x [OUTPUT] the x position of the window + * @param y [OUTPUT] the y position of the window + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_window_getPosition(RGFW_window* win, i32* x, i32* y); /*!< */ + +/**! + * @brief gets the size of the window | with RGFW_window.w and window.h + * @param win a pointer to the window + * @param w [OUTPUT] the width of the window + * @param h [OUTPUT] the height of the window + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_window_getSize(RGFW_window* win, i32* w, i32* h); + +/**! + * @brief gets the size of the window in exact pixels + * @param win a pointer to the window + * @param w [OUTPUT] the width of the window + * @param h [OUTPUT] the height of the window + * @return a bool if the function was successful +*/ +RGFWDEF RGFW_bool RGFW_window_getSizeInPixels(RGFW_window* win, i32* w, i32* h); + +/**! + * @brief gets the flags of the window | returns RGFW_window._flags + * @param win a pointer to the window + * @return the window flags +*/ +RGFWDEF u32 RGFW_window_getFlags(RGFW_window* win); + +/**! + * @brief returns the exit key assigned to the window + * @param win a pointer to the target window + * @return The key code assigned as the exit key +*/ +RGFWDEF RGFW_key RGFW_window_getExitKey(RGFW_window* win); + +/**! + * @brief sets the exit key for the window + * @param win a pointer to the target window + * @param key the key code to assign as the exit key +*/ +RGFWDEF void RGFW_window_setExitKey(RGFW_window* win, RGFW_key key); + +/**! + * @brief sets the types of events you want the window to receive + * @param win a pointer to the target window + * @param events the event flags to enable (use RGFW_allEventFlags for all) +*/ +RGFWDEF void RGFW_window_setEnabledEvents(RGFW_window* win, RGFW_eventFlag events); + +/**! + * @brief gets the currently enabled events for the window + * @param win a pointer to the target window + * @return The enabled event flags for the window +*/ +RGFWDEF RGFW_eventFlag RGFW_window_getEnabledEvents(RGFW_window* win); + +/**! + * @brief enables all events and disables selected ones + * @param win a pointer to the target window + * @param events the event flags to disable +*/ +RGFWDEF void RGFW_window_setDisabledEvents(RGFW_window* win, RGFW_eventFlag events); + +/**! + * @brief directly enables or disables a specific event or group of events + * @param win a pointer to the target window + * @param event the event flag or group of flags to modify + * @param state RGFW_TRUE to enable, RGFW_FALSE to disable +*/ +RGFWDEF void RGFW_window_setEventState(RGFW_window* win, RGFW_eventFlag event, RGFW_bool state); + +/**! + * @brief gets the user pointer associated with the window + * @param win a pointer to the target window + * @return The user-defined pointer stored in the window +*/ +RGFWDEF void* RGFW_window_getUserPtr(RGFW_window* win); + +/**! + * @brief sets a user pointer for the window + * @param win a pointer to the target window + * @param ptr a pointer to associate with the window +*/ +RGFWDEF void RGFW_window_setUserPtr(RGFW_window* win, void* ptr); + +/**! + * @brief retrieves the platform-specific window source pointer + * @param win a pointer to the target window + * @return A pointer to the internal RGFW_window_src structure +*/ +RGFWDEF RGFW_window_src* RGFW_window_getSrc(RGFW_window* win); + +/**! + * @brief sets the macOS layer object associated with the window + * @param win a pointer to the target window + * @param layer a pointer to the macOS layer object + * @note Only available on macOS platforms +*/ +RGFWDEF void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer); + +/**! + * @brief retrieves the macOS view object associated with the window + * @param win a pointer to the target window + * @return A pointer to the macOS view object, or NULL if not on macOS +*/ +RGFWDEF void* RGFW_window_getView_OSX(RGFW_window* win); + +/**! + * @brief retrieves the macOS window object + * @param win a pointer to the target window + * @return A pointer to the macOS window object, or NULL if not on macOS +*/ +RGFWDEF void* RGFW_window_getWindow_OSX(RGFW_window* win); + +/**! + * @brief retrieves the HWND handle for the window + * @param win a pointer to the target window + * @return A pointer to the Windows HWND handle, or NULL if not on Windows +*/ +RGFWDEF void* RGFW_window_getHWND(RGFW_window* win); + +/**! + * @brief retrieves the HDC handle for the window + * @param win a pointer to the target window + * @return A pointer to the Windows HDC handle, or NULL if not on Windows +*/ +RGFWDEF void* RGFW_window_getHDC(RGFW_window* win); + +/**! + * @brief retrieves the X11 Window handle for the window + * @param win a pointer to the target window + * @return The X11 Window handle, or 0 if not on X11 +*/ +RGFWDEF u64 RGFW_window_getWindow_X11(RGFW_window* win); + +/**! + * @brief retrieves the Wayland surface handle for the window + * @param win a pointer to the target window + * @return A pointer to the Wayland wl_surface, or NULL if not on Wayland +*/ +RGFWDEF struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win); + +/** * @defgroup Window_management +* @{ */ + +/*! set the window flags (will undo flags if they don't match the old ones) */ +RGFWDEF void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags); + +/**! + * @brief polls and pops the next event with the matching target window in event queue, pushes back events that don't match + * @param win a pointer to the target window + * @param event [OUTPUT] a pointer to store the retrieved event + * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise + * + * NOTE: Using this function without a loop may cause event lag. + * For multi-threaded systems, use RGFW_pollEvents combined with RGFW_window_checkQueuedEvent. + * + * Example: + * RGFW_event event; + * while (RGFW_window_checkEvent(win, &event)) { + * // handle event + * } +*/ +RGFWDEF RGFW_bool RGFW_window_checkEvent(RGFW_window* win, RGFW_event* event); + +/**! + * @brief pops the first queued event with the matching target window, pushes back events that don't match + * @param win a pointer to the target window + * @param event [OUTPUT] a pointer to store the retrieved event + * @return RGFW_TRUE if an event was found, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_checkQueuedEvent(RGFW_window* win, RGFW_event* event); + +/**! + * @brief checks if a key was pressed while the window is in focus + * @param win a pointer to the target window + * @param key the key code to check + * @return RGFW_TRUE if the key was pressed, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isKeyPressed(RGFW_window* win, RGFW_key key); + +/**! + * @brief checks if a key is currently being held down + * @param win a pointer to the target window + * @param key the key code to check + * @return RGFW_TRUE if the key is held down, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isKeyDown(RGFW_window* win, RGFW_key key); + +/**! + * @brief checks if a key was released + * @param win a pointer to the target window + * @param key the key code to check + * @return RGFW_TRUE if the key was released, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isKeyReleased(RGFW_window* win, RGFW_key key); + +/**! + * @brief checks if a mouse button was pressed + * @param win a pointer to the target window + * @param button the mouse button code to check + * @return RGFW_TRUE if the mouse button was pressed, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isMousePressed(RGFW_window* win, RGFW_mouseButton button); + +/**! + * @brief checks if a mouse button is currently held down + * @param win a pointer to the target window + * @param button the mouse button code to check + * @return RGFW_TRUE if the mouse button is down, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isMouseDown(RGFW_window* win, RGFW_mouseButton button); + +/**! + * @brief checks if a mouse button was released + * @param win a pointer to the target window + * @param button the mouse button code to check + * @return RGFW_TRUE if the mouse button was released, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isMouseReleased(RGFW_window* win, RGFW_mouseButton button); + +/**! + * @brief checks if the mouse left the window (true only for the first frame) + * @param win a pointer to the target window + * @return RGFW_TRUE if the mouse left, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_didMouseLeave(RGFW_window* win); + +/**! + * @brief checks if the mouse entered the window (true only for the first frame) + * @param win a pointer to the target window + * @return RGFW_TRUE if the mouse entered, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_didMouseEnter(RGFW_window* win); + +/**! + * @brief checks if the mouse is currently inside the window bounds + * @param win a pointer to the target window + * @return RGFW_TRUE if the mouse is inside, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isMouseInside(RGFW_window* win); + +/**! + * @brief checks if there is data being dragged into or within the window + * @param win a pointer to the target window + * @return RGFW_TRUE if data is being dragged, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isDataDragging(RGFW_window* win); + +/**! + * @brief gets the position of a data drag + * @param win a pointer to the target window + * @param x [OUTPUT] pointer to store the x position + * @param y [OUTPUT] pointer to store the y position + * @return RGFW_TRUE if there is an active drag, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_getDataDrag(RGFW_window* win, i32* x, i32* y); + +/**! + * @brief checks if a data drop occurred in the window (first frame only) + * @param win a pointer to the target window + * @return RGFW_TRUE if data was dropped, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_didDataDrop(RGFW_window* win); + +/**! + * @brief retrieves files from a data drop (drag and drop) + * @param win a pointer to the target window + * @param files [OUTPUT] a pointer to the array of file paths + * @param count [OUTPUT] the number of dropped files + * @return RGFW_TRUE if a data drop occurred, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_getDataDrop(RGFW_window* win, const char*** files, size_t* count); + +/**! + * @brief closes the window and frees its associated structure + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_close(RGFW_window* win); + +/**! + * @brief closes the window without freeing its structure + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_closePtr(RGFW_window* win); + +/**! + * @brief moves the window to a new position on the screen + * @param win a pointer to the target window + * @param x the new x position + * @param y the new y position +*/ +RGFWDEF void RGFW_window_move(RGFW_window* win, i32 x, i32 y); + +#ifndef RGFW_NO_MONITOR +/**! + * @brief moves the window to a specific monitor + * @param win a pointer to the target window + * @param m the target monitor +*/ +RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m); +#endif + +/**! + * @brief resizes the window to the given dimensions + * @param win a pointer to the target window + * @param w the new width + * @param h the new height +*/ +RGFWDEF void RGFW_window_resize(RGFW_window* win, i32 w, i32 h); + +/**! + * @brief sets the aspect ratio of the window + * @param win a pointer to the target window + * @param w the width ratio + * @param h the height ratio +*/ +RGFWDEF void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h); + +/**! + * @brief sets the minimum size of the window + * @param win a pointer to the target window + * @param w the minimum width + * @param h the minimum height +*/ +RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h); + +/**! + * @brief sets the maximum size of the window + * @param win a pointer to the target window + * @param w the maximum width + * @param h the maximum height +*/ +RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h); + +/**! + * @brief sets focus to the window + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_focus(RGFW_window* win); + +/**! + * @brief checks if the window is currently in focus + * @param win a pointer to the target window + * @return RGFW_TRUE if the window is in focus, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_isInFocus(RGFW_window* win); + +/**! + * @brief raises the window to the top of the stack + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_raise(RGFW_window* win); + +/**! + * @brief maximizes the window + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_maximize(RGFW_window* win); + +/**! + * @brief toggles fullscreen mode for the window + * @param win a pointer to the target window + * @param fullscreen RGFW_TRUE to enable fullscreen, RGFW_FALSE to disable +*/ +RGFWDEF void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen); + +/**! + * @brief centers the window on the screen + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_center(RGFW_window* win); + +/**! + * @brief minimizes the window + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_minimize(RGFW_window* win); + +/**! + * @brief restores the window from minimized state + * @param win a pointer to the target window +*/ +RGFWDEF void RGFW_window_restore(RGFW_window* win); + +/**! + * @brief makes the window a floating window + * @param win a pointer to the target window + * @param floating RGFW_TRUE to float, RGFW_FALSE to disable +*/ +RGFWDEF void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating); + +/**! + * @brief sets the opacity level of the window + * @param win a pointer to the target window + * @param opacity the opacity level (0–255) +*/ +RGFWDEF void RGFW_window_setOpacity(RGFW_window* win, u8 opacity); + +/**! + * @brief toggles window borders + * @param win a pointer to the target window + * @param border RGFW_TRUE for bordered, RGFW_FALSE for borderless +*/ +RGFWDEF void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border); + +/**! + * @brief checks if the window is borderless + * @param win a pointer to the target window + * @return RGFW_TRUE if borderless, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_borderless(RGFW_window* win); + +/**! + * @brief toggles drag-and-drop (DND) support for the window + * @param win a pointer to the target window + * @param allow RGFW_TRUE to allow DND, RGFW_FALSE to disable + * @note RGFW_windowAllowDND must still be passed when creating the window +*/ +RGFWDEF void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow); + +/**! + * @brief checks if drag-and-drop (DND) is allowed + * @param win a pointer to the target window + * @return RGFW_TRUE if DND is enabled, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_allowsDND(RGFW_window* win); + +#ifndef RGFW_NO_PASSTHROUGH +/**! + * @brief toggles mouse passthrough for the window + * @param win a pointer to the target window + * @param passthrough RGFW_TRUE to enable passthrough, RGFW_FALSE to disable +*/ +RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough); +#endif + +/**! + * @brief renames the window + * @param win a pointer to the target window + * @param name the new title string for the window +*/ +RGFWDEF void RGFW_window_setName(RGFW_window* win, const char* name); + +/**! + * @brief sets the icon for the window and taskbar + * @param win a pointer to the target window + * @param data the image data + * @param w the width of the icon + * @param h the height of the icon + * @param format the image format + * @return RGFW_TRUE if successful, RGFW_FALSE otherwise + * + * NOTE: The image may be resized by default. +*/ +RGFWDEF RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format); + +/**! + * @brief sets the icon for the window and/or taskbar + * @param win a pointer to the target window + * @param data the image data + * @param w the width of the icon + * @param h the height of the icon + * @param format the image format + * @param type the target icon type (taskbar, window, or both) + * @return RGFW_TRUE if successful, RGFW_FALSE otherwise +*/ +RGFWDEF RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type); + +/**! + * @brief sets the mouse icon for the window using a loaded bitmap + * @param win a pointer to the target window + * @param mouse a pointer to the RGFW_mouse struct containing the icon +*/ +RGFWDEF void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse); + +/**! + * @brief Sets the mouse to a standard system cursor. + * @param win The target window. + * @param mouse The standard cursor type (see RGFW_MOUSE enum). + * @return True if the standard cursor was successfully applied. +*/ +RGFWDEF RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, RGFW_mouseIcons mouse); + +/**! + * @brief Sets the mouse to the default cursor icon. + * @param win The target window. + * @return True if the default cursor was successfully set. +*/ +RGFWDEF RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win); + +/**! + * @brief set (enable or disable) raw mouse mode only for the select window + * @param win The target window. + * @param the boolean state of raw mouse mode + * +*/ +RGFWDEF void RGFW_window_setRawMouseMode(RGFW_window* win, RGFW_bool state); + +/**! + * @brief lock/unlock the cursor. + * @param win The target window. + * @param the boolean state of the mouse's capture state + * +*/ +RGFWDEF void RGFW_window_captureMouse(RGFW_window* win, RGFW_bool state); + +/**! + * @brief lock/unlock the cursor and enable raw mpuise mode. + * @param win The target window. + * @param the boolean state of raw mouse mode + * +*/ +RGFWDEF void RGFW_window_captureRawMouse(RGFW_window* win, RGFW_bool state); + +/**! + * @brief Locks the cursor to the center of the window. + * @param win The target window. + * @warning this function is deprecated and will be replaced by RGFW_window_captureRawMouse in RGFW 2.0 + * +*/ +RGFWDEF void RGFW_window_holdMouse(RGFW_window* win); + +/**! + * @brief Releases the mouse so it can move freely again. + * @param win The target window. + * @warning this function is deprecated and will be replaced by RGFW_window_captureRawMouse in RGFW 2.0 +*/ +RGFWDEF void RGFW_window_unholdMouse(RGFW_window* win); + +/**! + * @brief Returns true if the mouse is using raw mouse mode + * @param win The target window. + * @return True if the mouse is using raw mouse input mode. +*/ +RGFWDEF RGFW_bool RGFW_window_isRawMouseMode(RGFW_window* win); + + +/**! + * @brief Returns true if the mouse is captured + * @param win The target window. + * @return True if the mouse is being captured. +*/ +RGFWDEF RGFW_bool RGFW_window_isCaptured(RGFW_window* win); + +/**! + * @brief Returns true if the mouse is currently held by RGFW. + * @param win The target window. + * @return True if the mouse is being held. + * @warning this function is deprecated and will be replaced by RGFW_window_isHoldingMouse in RGFW 2.0 +*/ +RGFWDEF RGFW_bool RGFW_window_isHoldingMouse(RGFW_window* win); + +/**! + * @brief Hides the window from view. + * @param win The target window. +*/ +RGFWDEF void RGFW_window_hide(RGFW_window* win); + +/**! + * @brief Shows the window if it was hidden. + * @param win The target window. +*/ +RGFWDEF void RGFW_window_show(RGFW_window* win); + +/**! + * @breif request a window flash to get attention from the user + * @param win the target window + * @param request the flash operation requested +*/ +RGFWDEF void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request); + +/**! + * @brief Sets whether the window should close. + * @param win The target window. + * @param shouldClose True to signal the window should close, false to keep it open. + * + * This can override or trigger the `RGFW_window_shouldClose` state by modifying window flags. +*/ +RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose); + +/**! + * @brief Retrieves the current global mouse position. + * @param x [OUTPUT] Pointer to store the X position of the mouse on the screen. + * @param y [OUTPUT] Pointer to store the Y position of the mouse on the screen. + * @return True if the position was successfully retrieved. +*/ +RGFWDEF RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y); + +/**! + * @brief Retrieves the mouse position relative to the window. + * @param win The target window. + * @param x [OUTPUT] Pointer to store the X position within the window. + * @param y [OUTPUT] Pointer to store the Y position within the window. + * @return True if the position was successfully retrieved. +*/ +RGFWDEF RGFW_bool RGFW_window_getMouse(RGFW_window* win, i32* x, i32* y); + +/**! + * @brief Shows or hides the mouse cursor for the window. + * @param win The target window. + * @param show True to show the mouse, false to hide it. +*/ +RGFWDEF void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show); + +/**! + * @brief Checks if the mouse is currently hidden in the window. + * @param win The target window. + * @return True if the mouse is hidden. +*/ +RGFWDEF RGFW_bool RGFW_window_isMouseHidden(RGFW_window* win); + +/**! + * @brief Moves the mouse to the specified position within the window. + * @param win The target window. + * @param x The new X position. + * @param y The new Y position. +*/ +RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y); + +/**! + * @brief Checks if the window should close. + * @param win The target window. + * @return True if the window should close (for example, if ESC was pressed or a close event occurred). +*/ +RGFWDEF RGFW_bool RGFW_window_shouldClose(RGFW_window* win); + +/**! + * @brief Checks if the window is currently fullscreen. + * @param win The target window. + * @return True if the window is fullscreen. +*/ +RGFWDEF RGFW_bool RGFW_window_isFullscreen(RGFW_window* win); + +/**! + * @brief Checks if the window is currently hidden. + * @param win The target window. + * @return True if the window is hidden. +*/ +RGFWDEF RGFW_bool RGFW_window_isHidden(RGFW_window* win); + +/**! + * @brief Checks if the window is minimized. + * @param win The target window. + * @return True if the window is minimized. +*/ +RGFWDEF RGFW_bool RGFW_window_isMinimized(RGFW_window* win); + +/**! + * @brief Checks if the window is maximized. + * @param win The target window. + * @return True if the window is maximized. +*/ +RGFWDEF RGFW_bool RGFW_window_isMaximized(RGFW_window* win); + +/**! + * @brief Checks if the window is floating. + * @param win The target window. + * @return True if the window is floating. +*/ +RGFWDEF RGFW_bool RGFW_window_isFloating(RGFW_window* win); +/** @} */ + +/** * @defgroup Monitor +* @{ */ + +#ifndef RGFW_NO_MONITOR +/**! + * @brief Scales the window to match its monitor’s resolution. + * @param win The target window. + * + * This function is automatically called when the flag `RGFW_scaleToMonitor` + * is used during window creation. +*/ +RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win); + +/**! + * @brief Retrieves the monitor structure associated with the window. + * @param win The target window. + * @return The monitor structure of the window. +*/ +RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win); +#endif + +/** @} */ + +/** * @defgroup Clipboard +* @{ */ + +/**! + * @brief Reads clipboard data. + * @param size [OUTPUT] A pointer that will be filled with the size of the clipboard data. + * @return A pointer to the clipboard data as a string. +*/ +RGFWDEF const char* RGFW_readClipboard(size_t* size); + +/**! + * @brief Reads clipboard data into a provided buffer, or returns the required length if str is NULL. + * @param str [OUTPUT] A pointer to the buffer that will receive the clipboard data (or NULL to get required size). + * @param strCapacity The capacity of the provided buffer. + * @return The number of bytes read or required length of clipboard data. +*/ +RGFWDEF RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity); + +/**! + * @brief Writes text to the clipboard. + * @param text The text to be written to the clipboard. + * @param textLen The length of the text being written. +*/ +RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); +/** @} */ + + + +/** * @defgroup error handling +* @{ */ +/**! + * @brief Sets the callback function to handle debug messages from RGFW. + * @param func The function pointer to be used as the debug callback. + * @return The previously set debug callback function. +*/ +RGFWDEF RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func); + +/**! + * @brief Sends a debug message manually through the currently set debug callback. + * @param type The type of debug message being sent. + * @param err The associated error code. + * @param msg The debug message text. +*/ +RGFWDEF void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, const char* msg); +/** @} */ + +/** + + + event callbacks. + These are completely optional, so you can use the normal + RGFW_checkEvent() method if you prefer that + +* @defgroup Callbacks +* @{ +*/ + +/**! + * @brief Sets the callback function for window move events. + * @param func The function to be called when the window is moved. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowMovedfunc RGFW_setWindowMovedCallback(RGFW_windowMovedfunc func); + +/**! + * @brief Sets the callback function for window resize events. + * @param func The function to be called when the window is resized. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowResizedfunc RGFW_setWindowResizedCallback(RGFW_windowResizedfunc func); + +/**! + * @brief Sets the callback function for window quit events. + * @param func The function to be called when the window receives a quit signal. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowQuitfunc RGFW_setWindowQuitCallback(RGFW_windowQuitfunc func); + +/**! + * @brief Sets the callback function for mouse move events. + * @param func The function to be called when the mouse moves within the window. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_mousePosfunc RGFW_setMousePosCallback(RGFW_mousePosfunc func); + +/**! + * @brief Sets the callback function for window refresh events. + * @param func The function to be called when the window needs to be refreshed. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowRefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowRefreshfunc func); + +/**! + * @brief Sets the callback function for focus change events. + * @param func The function to be called when the window gains or loses focus. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func); + +/**! + * @brief Sets the callback function for mouse notification events. + * @param func The function to be called when a mouse notification event occurs. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallback(RGFW_mouseNotifyfunc func); + +/**! + * @brief Sets the callback function for data drop events. + * @param func The function to be called when data is dropped into the window. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_dataDropfunc RGFW_setDataDropCallback(RGFW_dataDropfunc func); + +/**! + * @brief Sets the callback function for the start of a data drag event. + * @param func The function to be called when data dragging begins. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_dataDragfunc RGFW_setDataDragCallback(RGFW_dataDragfunc func); + +/**! + * @brief Sets the callback function for key press and release events. + * @param func The function to be called when a key is pressed or released. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func); + +/**! + * @brief Sets the callback function for mouse button press and release events. + * @param func The function to be called when a mouse button is pressed or released. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_mouseButtonfunc RGFW_setMouseButtonCallback(RGFW_mouseButtonfunc func); + +/**! + * @brief Sets the callback function for mouse scroll events. + * @param func The function to be called when the mouse wheel is scrolled. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_mouseScrollfunc RGFW_setMouseScrollCallback(RGFW_mouseScrollfunc func); + +/**! + * @brief Sets the callback function for window maximize events. + * @param func The function to be called when the window is maximized. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowMaximizedfunc RGFW_setWindowMaximizedCallback(RGFW_windowMaximizedfunc func); + +/**! + * @brief Sets the callback function for window minimize events. + * @param func The function to be called when the window is minimized. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowMinimizedfunc RGFW_setWindowMinimizedCallback(RGFW_windowMinimizedfunc func); + +/**! + * @brief Sets the callback function for window restore events. + * @param func The function to be called when the window is restored from a minimized or maximized state. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_windowRestoredfunc RGFW_setWindowRestoredCallback(RGFW_windowRestoredfunc func); + +/**! + * @brief Sets the callback function for DPI (scale) update events. + * @param func The function to be called when the window’s DPI or scale changes. + * @return The previously set callback function, if any. +*/ +RGFWDEF RGFW_scaleUpdatedfunc RGFW_setScaleUpdatedCallback(RGFW_scaleUpdatedfunc func); +/** @} */ + +/** * @defgroup graphics_API +* @{ */ + +/*! native rendering API functions */ +#if defined(RGFW_OPENGL) +/* these are native opengl specific functions and will NOT work with EGL */ + +/*!< make the window the current OpenGL drawing context + + NOTE: + if you want to switch the graphics context's thread, + you have to run RGFW_window_makeCurrentContext_OpenGL(NULL); on the old thread + then RGFW_window_makeCurrentContext_OpenGL(valid_window) on the new thread +*/ + +/**! + * @brief Sets the global OpenGL hints to the specified pointer. + * @param hints A pointer to the RGFW_glHints structure containing the desired OpenGL settings. +*/ +RGFWDEF void RGFW_setGlobalHints_OpenGL(RGFW_glHints* hints); + +/**! + * @brief Resets the global OpenGL hints to their default values. +*/ +RGFWDEF void RGFW_resetGlobalHints_OpenGL(void); + +/**! + * @brief Gets the current global OpenGL hints pointer. + * @return A pointer to the currently active RGFW_glHints structure. +*/ +RGFWDEF RGFW_glHints* RGFW_getGlobalHints_OpenGL(void); + +/**! + * @brief Creates and allocates an OpenGL context for the specified window. + * @param win A pointer to the target RGFW_window. + * @param hints A pointer to an RGFW_glHints structure defining context creation parameters. + * @return A pointer to the newly created RGFW_glContext. +*/ +RGFWDEF RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints); + +/**! + * @brief Creates an OpenGL context for the specified window using a preallocated context structure. + * @param win A pointer to the target RGFW_window. + * @param ctx A pointer to an already allocated RGFW_glContext structure. + * @param hints A pointer to an RGFW_glHints structure defining context creation parameters. + * @return RGFW_TRUE on success, RGFW_FALSE on failure. +*/ +RGFWDEF RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints); + +/**! + * @brief Retrieves the OpenGL context associated with a window. + * @param win A pointer to the RGFW_window. + * @return A pointer to the associated RGFW_glContext, or NULL if none exists or if the context is EGL-based. +*/ +RGFWDEF RGFW_glContext* RGFW_window_getContext_OpenGL(RGFW_window* win); + +/**! + * @brief Deletes and frees the OpenGL context. + * @param win A pointer to the RGFW_window. + * @param ctx A pointer to the RGFW_glContext to delete. + * + * @note This is automatically called by RGFW_window_close if the window’s context is not NULL. +*/ +RGFWDEF void RGFW_window_deleteContext_OpenGL(RGFW_window* win, RGFW_glContext* ctx); + +/**! + * @brief Deletes the OpenGL context without freeing its memory. + * @param win A pointer to the RGFW_window. + * @param ctx A pointer to the RGFW_glContext to delete. + * + * @note This is automatically called by RGFW_window_close if the window’s context is not NULL. +*/ +RGFWDEF void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx); + +/**! + * @brief Retrieves the native source context from an RGFW_glContext. + * @param ctx A pointer to the RGFW_glContext. + * @return A pointer to the native OpenGL context handle. +*/ +RGFWDEF void* RGFW_glContext_getSourceContext(RGFW_glContext* ctx); + +/**! + * @brief Makes the specified window the current OpenGL rendering target. + * @param win A pointer to the RGFW_window to make current. + * + * @note This is typically called internally by RGFW_window_makeCurrent. +*/ +RGFWDEF void RGFW_window_makeCurrentWindow_OpenGL(RGFW_window* win); + +/**! + * @brief Makes the OpenGL context of the specified window current. + * @param win A pointer to the RGFW_window whose context should be made current. + * + * @note To move a context between threads, call RGFW_window_makeCurrentContext_OpenGL(NULL) + * on the old thread before making it current on the new one. +*/ +RGFWDEF void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win); + +/**! + * @brief Swaps the OpenGL buffers for the specified window. + * @param win A pointer to the RGFW_window whose buffers should be swapped. +*/ +RGFWDEF void RGFW_window_swapBuffers_OpenGL(RGFW_window* win); + +/**! + * @brief Retrieves the current OpenGL context. + * @return A pointer to the currently active OpenGL context (GLX, WGL, Cocoa, or WebGL backend). +*/ +RGFWDEF void* RGFW_getCurrentContext_OpenGL(void); + +/**! + * @brief Retrieves the current OpenGL window. + * @return A pointer to the RGFW_window currently bound as the OpenGL context target. +*/ +RGFWDEF RGFW_window* RGFW_getCurrentWindow_OpenGL(void); + +/**! + * @brief Sets the OpenGL swap interval (vsync). + * @param win A pointer to the RGFW_window. + * @param swapInterval The desired swap interval value (0 to disable vsync, 1 to enable). +*/ +RGFWDEF void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval); + +/**! + * @brief Retrieves the address of a native OpenGL procedure. + * @param procname The name of the OpenGL function to look up. + * @return A pointer to the function, or NULL if not found. +*/ +RGFWDEF RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname); + +/**! + * @brief Checks whether a specific OpenGL or OpenGL ES API extension is supported. + * @param extension The name of the extension to check. + * @param len The length of the extension string. + * @return RGFW_TRUE if supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_extensionSupported_OpenGL(const char* extension, size_t len); + +/**! + * @brief Checks whether a specific platform-dependent OpenGL extension is supported. + * @param extension The name of the extension to check. + * @param len The length of the extension string. + * @return RGFW_TRUE if supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len); + +/* these are EGL specific functions, they may fallback to OpenGL */ +#ifdef RGFW_EGL +/**! + * @brief Creates and allocates an OpenGL/EGL context for the specified window. + * @param win A pointer to the target RGFW_window. + * @param hints A pointer to an RGFW_glHints structure defining context creation parameters. + * @return A pointer to the newly created RGFW_eglContext. +*/ +RGFWDEF RGFW_eglContext* RGFW_window_createContext_EGL(RGFW_window* win, RGFW_glHints* hints); + +/**! + * @brief Creates an OpenGL/EGL context for the specified window using a preallocated context structure. + * @param win A pointer to the target RGFW_window. + * @param ctx A pointer to an already allocated RGFW_eglContext structure. + * @param hints A pointer to an RGFW_glHints structure defining context creation parameters. + * @return RGFW_TRUE on success, RGFW_FALSE on failure. +*/ +RGFWDEF RGFW_bool RGFW_window_createContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx, RGFW_glHints* hints); + +/**! + * @brief Frees and deletes an OpenGL/EGL context. + * @param win A pointer to the RGFW_window. + * @param ctx A pointer to the RGFW_eglContext to delete. + * + * @note Automatically called by RGFW_window_close if RGFW owns the context. +*/ +RGFWDEF void RGFW_window_deleteContext_EGL(RGFW_window* win, RGFW_eglContext* ctx); + +/**! + * @brief Deletes an OpenGL/EGL context without freeing its memory. + * @param win A pointer to the RGFW_window. + * @param ctx A pointer to the RGFW_eglContext to delete. + * + * @note Automatically called by RGFW_window_close if RGFW owns the context. +*/ +RGFWDEF void RGFW_window_deleteContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx); + +/**! + * @brief Retrieves the OpenGL/EGL context associated with a window. + * @param win A pointer to the RGFW_window. + * @return A pointer to the associated RGFW_eglContext, or NULL if none exists or if the context is a native OpenGL context. +*/ +RGFWDEF RGFW_eglContext* RGFW_window_getContext_EGL(RGFW_window* win); + +/**! + * @brief Retrieves the EGL display handle. + * @return A pointer to the native EGLDisplay. +*/ +RGFWDEF void* RGFW_getDisplay_EGL(void); + +/**! + * @brief Retrieves the native source context from an RGFW_eglContext. + * @param ctx A pointer to the RGFW_eglContext. + * @return A pointer to the native EGLContext handle. +*/ +RGFWDEF void* RGFW_eglContext_getSourceContext(RGFW_eglContext* ctx); + +/**! + * @brief Retrieves the EGL surface handle from an RGFW_eglContext. + * @param ctx A pointer to the RGFW_eglContext. + * @return A pointer to the EGLSurface associated with the context. +*/ +RGFWDEF void* RGFW_eglContext_getSurface(RGFW_eglContext* ctx); + +/**! + * @brief Retrieves the Wayland EGL window handle from an RGFW_eglContext. + * @param ctx A pointer to the RGFW_eglContext. + * @return A pointer to the wl_egl_window associated with the EGL context. +*/ +RGFWDEF struct wl_egl_window* RGFW_eglContext_wlEGLWindow(RGFW_eglContext* ctx); + +/**! + * @brief Swaps the EGL buffers for the specified window. + * @param win A pointer to the RGFW_window whose buffers should be swapped. + * + * @note Typically called by RGFW_window_swapInterval. +*/ +RGFWDEF void RGFW_window_swapBuffers_EGL(RGFW_window* win); + +/**! + * @brief Makes the specified window the current EGL rendering target. + * @param win A pointer to the RGFW_window to make current. + * + * @note This is typically called internally by RGFW_window_makeCurrent. +*/ +RGFWDEF void RGFW_window_makeCurrentWindow_EGL(RGFW_window* win); + +/**! + * @brief Makes the EGL context of the specified window current. + * @param win A pointer to the RGFW_window whose context should be made current. + * + * @note To move a context between threads, call RGFW_window_makeCurrentContext_EGL(NULL) + * on the old thread before making it current on the new one. +*/ +RGFWDEF void RGFW_window_makeCurrentContext_EGL(RGFW_window* win); + +/**! + * @brief Retrieves the current EGL context. + * @return A pointer to the currently active EGLContext. +*/ +RGFWDEF void* RGFW_getCurrentContext_EGL(void); + +/**! + * @brief Retrieves the current EGL window. + * @return A pointer to the RGFW_window currently bound as the EGL context target. +*/ +RGFWDEF RGFW_window* RGFW_getCurrentWindow_EGL(void); + +/**! + * @brief Sets the EGL swap interval (vsync). + * @param win A pointer to the RGFW_window. + * @param swapInterval The desired swap interval value (0 to disable vsync, 1 to enable). +*/ +RGFWDEF void RGFW_window_swapInterval_EGL(RGFW_window* win, i32 swapInterval); + +/**! + * @brief Retrieves the address of a native OpenGL or OpenGL ES procedure in an EGL context. + * @param procname The name of the OpenGL function to look up. + * @return A pointer to the function, or NULL if not found. +*/ +RGFWDEF RGFW_proc RGFW_getProcAddress_EGL(const char* procname); + +/**! + * @brief Checks whether a specific OpenGL or OpenGL ES API extension is supported in the current EGL context. + * @param extension The name of the extension to check. + * @param len The length of the extension string. + * @return RGFW_TRUE if supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_extensionSupported_EGL(const char* extension, size_t len); + +/**! + * @brief Checks whether a specific platform-dependent EGL extension is supported in the current context. + * @param extension The name of the extension to check. + * @param len The length of the extension string. + * @return RGFW_TRUE if supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform_EGL(const char* extension, size_t len); +#endif +#endif + +#ifdef RGFW_VULKAN +#include + +/* if you don't want to use the above macros */ + +/**! + * @brief Retrieves the Vulkan instance extensions required by RGFW. + * @param count [OUTPUT] A pointer that will receive the number of required extensions (typically 2). + * @return A pointer to a static array of required Vulkan instance extension names. +*/ +RGFWDEF const char** RGFW_getRequiredInstanceExtensions_Vulkan(size_t* count); + +/**! + * @brief Creates a Vulkan surface for the specified window. + * @param win A pointer to the RGFW_window for which to create the Vulkan surface. + * @param instance The Vulkan instance used to create the surface. + * @param surface [OUTPUT] A pointer to a VkSurfaceKHR handle that will receive the created surface. + * @return A VkResult indicating success or failure. +*/ +RGFWDEF VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface); + +/**! + * @brief Checks whether the specified Vulkan physical device and queue family support presentation for RGFW. + * @param instance The Vulkan instance. + * @param physicalDevice The Vulkan physical device to check. + * @param queueFamilyIndex The index of the queue family to query for presentation support. + * @return RGFW_TRUE if presentation is supported, RGFW_FALSE otherwise. +*/ +RGFWDEF RGFW_bool RGFW_getPresentationSupport_Vulkan(VkInstance instance, VkPhysicalDevice physicalDevice, u32 queueFamilyIndex); +#endif + +#ifdef RGFW_DIRECTX +#ifndef RGFW_WINDOWS + #undef RGFW_DIRECTX +#else + #define OEMRESOURCE + #include + + #ifndef __cplusplus + #define __uuidof(T) IID_##T + #endif +/**! + * @brief Creates a DirectX swap chain for the specified RGFW window. + * @param win A pointer to the RGFW_window for which to create the swap chain. + * @param pFactory A pointer to the IDXGIFactory used to create the swap chain. + * @param pDevice A pointer to the DirectX device (e.g., ID3D11Device or ID3D12Device). + * @param swapchain [OUTPUT] A pointer to an IDXGISwapChain pointer that will receive the created swap chain. + * @return An integer result code (0 on success, or a DirectX error code on failure). +*/ +RGFWDEF int RGFW_window_createSwapChain_DirectX(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain); +#endif +#endif + +#ifdef RGFW_WEBGPU + #include + /**! + * @brief Creates a WebGPU surface for the specified RGFW window. + * @param window A pointer to the RGFW_window for which to create the surface. + * @param instance The WebGPU instance used to create the surface. + * @return The created WGPUSurface handle. + */ + RGFWDEF WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance); +#endif + +/** @} */ + +/** * @defgroup Supporting +* @{ */ + +/**! + * @brief Sets the root (main) RGFW window. + * @param win A pointer to the RGFW_window to set as the root window. +*/ +RGFWDEF void RGFW_setRootWindow(RGFW_window* win); + +/**! + * @brief Retrieves the current root RGFW window. + * @return A pointer to the current root RGFW_window. +*/ +RGFWDEF RGFW_window* RGFW_getRootWindow(void); + +/**! + * @brief Pushes an event into the standard RGFW event queue. + * @param event A pointer to the RGFW_event to be added to the queue. +*/ +RGFWDEF void RGFW_eventQueuePush(const RGFW_event* event); + +/**! + * @brief Clears all events from the RGFW event queue without processing them. +*/ +RGFWDEF void RGFW_eventQueueFlush(void); + +/**! + * @brief Pops the next event from the RGFW event queue. + * @return A pointer to the popped RGFW_event, or NULL if the queue is empty. +*/ +RGFWDEF RGFW_event* RGFW_eventQueuePop(void); + +/**! + * @brief Pops the next event from the RGFW event queue that matches the target window, pushes back events that don't matchj. + * @param win A pointer to the target RGFW_window. + * @return A pointer to the popped RGFW_event, or NULL if the queue is empty. +*/ +RGFWDEF RGFW_event* RGFW_window_eventQueuePop(RGFW_window* win); + +/**! + * @brief Converts an API keycode to the RGFW unmapped (physical) key. + * @param keycode The platform-specific keycode. + * @return The corresponding RGFW keycode. +*/ +RGFWDEF u32 RGFW_apiKeyToRGFW(u32 keycode); + +/**! + * @brief Converts an RGFW keycode to the unmapped (physical) API key. + * @param keycode The RGFW keycode. + * @return The corresponding platform-specific keycode. +*/ +RGFWDEF u32 RGFW_rgfwToApiKey(u32 keycode); + +/**! + * @brief Converts an RGFW keycode to the mapped character representation. + * @param keycode The RGFW keycode. + * @return The corresponding key character. +*/ +RGFWDEF u8 RGFW_rgfwToKeyChar(u32 keycode); + +/**! + * @brief Retrieves the size of the RGFW_info structure. + * @return The size (in bytes) of RGFW_info. +*/ +RGFWDEF size_t RGFW_sizeofInfo(void); + +/**! + * @brief Initializes the RGFW library. + * @return 0 on success, or a negative error code on failure. + * @note This is automatically called when the first window is created. +*/ +RGFWDEF i32 RGFW_init(void); + +/**! + * @brief Deinitializes the RGFW library. + * @note This is automatically called when the last open window is closed. +*/ +RGFWDEF void RGFW_deinit(void); + +/**! + * @brief Initializes RGFW using a user-provided RGFW_info structure. + * @param info A pointer to an RGFW_info structure to be used for initialization. + * @return 0 on success, or a negative error code on failure. +*/ +RGFWDEF i32 RGFW_init_ptr(RGFW_info* info); + +/**! + * @brief Deinitializes a specific RGFW instance stored in the provided RGFW_info pointer. + * @param info A pointer to the RGFW_info structure representing the instance to deinitialize. +*/ +RGFWDEF void RGFW_deinit_ptr(RGFW_info* info); + +/**! + * @brief Sets the global RGFW_info structure pointer. + * @param info A pointer to the RGFW_info structure to set. +*/ +RGFWDEF void RGFW_setInfo(RGFW_info* info); + +/**! + * @brief Retrieves the global RGFW_info structure pointer. + * @return A pointer to the current RGFW_info structure. +*/ +RGFWDEF RGFW_info* RGFW_getInfo(void); + +/** @} */ +#endif /* RGFW_HEADER */ + +#if !defined(RGFW_NATIVE_HEADER) && (defined(RGFW_NATIVE) || defined(RGFW_IMPLEMENTATION)) +#define RGFW_NATIVE_HEADER + #if (defined(RGFW_OPENGL) || defined(RGFW_WEGL)) && defined(_MSC_VER) + #pragma comment(lib, "opengl32") + #endif + + #ifdef RGFW_OPENGL + struct RGFW_eglContext { + void* ctx; + void* surface; + struct wl_egl_window* eglWindow; + }; + + typedef union RGFW_gfxContext { + RGFW_glContext* native; + RGFW_eglContext* egl; + } RGFW_gfxContext; + + typedef RGFW_ENUM(u32, RGFW_gfxContextType) { + RGFW_gfxNativeOpenGL = RGFW_BIT(0), + RGFW_gfxEGL = RGFW_BIT(1), + RGFW_gfxOwnedByRGFW = RGFW_BIT(2) + }; + #endif + + /*! source data for the window (used by the APIs) */ + #ifdef RGFW_WINDOWS + + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef OEMRESOURCE + #define OEMRESOURCE + #endif + + #include + + struct RGFW_nativeImage { + HBITMAP bitmap; + u8* bitmapBits; + RGFW_format format; + HDC hdcMem; + }; + + #ifdef RGFW_OPENGL + struct RGFW_glContext { HGLRC ctx; }; + #endif + + struct RGFW_window_src { + HWND window; /*!< source window */ + HDC hdc; /*!< source HDC */ + i32 offsetW, offsetH; /*!< width and height offset for window */ + HICON hIconSmall, hIconBig; /*!< source window icons */ + i32 maxSizeW, maxSizeH, minSizeW, minSizeH, aspectRatioW, aspectRatioH; /*!< for setting max/min resize (RGFW_WINDOWS) */ + RGFW_bool actionFrame; /* frame after a caption button was toggled (e.g. minimize, maximize or close) */ + #ifdef RGFW_OPENGL + RGFW_gfxContext ctx; + RGFW_gfxContextType gfxType; + #endif + }; + +#elif defined(RGFW_UNIX) + #ifdef RGFW_X11 + #include + #include + #endif + + #ifdef RGFW_WAYLAND + #ifdef RGFW_LIBDECOR + #include + #endif + + #include + #include + #endif + + struct RGFW_nativeImage { + #ifdef RGFW_X11 + XImage* bitmap; + #endif + #ifdef RGFW_WAYLAND + struct wl_buffer* wl_buffer; + #endif + u8* buffer; + RGFW_format format; + }; + + #ifdef RGFW_OPENGL + struct RGFW_glContext { + #ifdef RGFW_X11 + struct __GLXcontextRec* ctx; /*!< source graphics context */ + Window window; + #endif + #ifdef RGFW_WAYLAND + RGFW_eglContext egl; + #endif + }; + #endif + + struct RGFW_window_src { + i32 x, y, w, h; + #ifdef RGFW_OPENGL + RGFW_gfxContext ctx; + RGFW_gfxContextType gfxType; + #endif +#ifdef RGFW_X11 + Window window; /*!< source window */ + Window parent; /*!< parent window */ + GC gc; + u64 flashEnd; + #ifdef RGFW_ADVANCED_SMOOTH_RESIZE + i64 counter_value; + XID counter; + #endif +#endif /* RGFW_X11 */ + +#if defined(RGFW_WAYLAND) + struct wl_surface* surface; + struct xdg_surface* xdg_surface; + struct xdg_toplevel* xdg_toplevel; + struct zxdg_toplevel_decoration_v1* decoration; + struct zwp_locked_pointer_v1 *locked_pointer; + struct xdg_toplevel_icon_v1 *icon; + u32 decoration_mode; + /* State flags to configure the window */ + RGFW_bool pending_activated; + RGFW_bool activated; + RGFW_bool resizing; + RGFW_bool pending_maximized; + RGFW_bool maximized; + RGFW_bool minimized; + + RGFW_bool using_custom_cursor; + struct wl_surface* custom_cursor_surface; + + RGFW_monitor active_monitor; + + struct wl_data_source *data_source; // offer data to other clients + + #ifdef RGFW_LIBDECOR + struct libdecor* decorContext; + #endif +#endif /* RGFW_WAYLAND */ + }; + +#elif defined(RGFW_MACOS) + + struct RGFW_nativeImage { + RGFW_format format; + u8* buffer; + }; + + #ifdef RGFW_OPENGL + struct RGFW_glContext { void* ctx; }; + #endif + + struct RGFW_window_src { + void* window; + void* view; /* apple viewpoint thingy */ + void* mouse; + #ifdef RGFW_OPENGL + RGFW_gfxContext ctx; + RGFW_gfxContextType gfxType; + #endif + }; + +#elif defined(RGFW_WASM) + + #include + #include + + struct RGFW_nativeImage { + RGFW_format format; + }; + + #ifdef RGFW_OPENGL + struct RGFW_glContext { + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx; + }; + #endif + + struct RGFW_window_src { + #ifdef RGFW_OPENGL + RGFW_gfxContext ctx; + RGFW_gfxContextType gfxType; + #endif + }; + +#endif + +struct RGFW_surface { + u8* data; + i32 w, h; + RGFW_format format; + RGFW_nativeImage native; +}; + +/*! internal window data that is not specific to the OS */ +typedef struct RGFW_windowInternal { + /*! which key RGFW_window_shouldClose checks. Settting this to RGFW_keyNULL disables the feature. */ + RGFW_key exitKey; + i32 lastMouseX, lastMouseY; /*!< last cusor point (for raw mouse data) */ + + RGFW_bool shouldClose; + RGFW_bool rawMouse; + RGFW_bool captureMouse; + RGFW_bool inFocus; + RGFW_bool mouseInside; + RGFW_keymod mod; + RGFW_eventFlag enabledEvents; + u32 flags; /*!< windows flags (for RGFW to check and modify) */ + i32 oldX, oldY, oldW, oldH; +} RGFW_windowInternal; + +struct RGFW_window { + RGFW_window_src src; /*!< src window data */ + RGFW_windowInternal internal; /*!< internal window data that is not specific to the OS */ + void* userPtr; /* ptr for usr data */ + i32 x, y, w, h; /*!< position and size of the window */ +}; /*!< window structure for the window */ + +typedef struct RGFW_windowState { + RGFW_bool mouseEnter; + RGFW_bool dataDragging; + RGFW_bool dataDrop; + size_t filesCount; + i32 dropX, dropY; + RGFW_window* win; /*!< it's not possible for one of these events to happen in the frame that the other event happened */ + + RGFW_bool mouseLeave; + RGFW_window* winLeave; /*!< if a mouse leaves one window and enters the next */ +} RGFW_windowState; + +typedef struct { + RGFW_bool current; + RGFW_bool prev; +} RGFW_keyState; + +#ifndef RGFW_NO_MONITOR + typedef struct RGFW_monitorNode { + RGFW_monitor mon; + struct RGFW_monitorNode* next; +#ifdef RGFW_WAYLAND + u32 id; /* Add id so wl_outputs can be removed */ + struct wl_output *output; + struct zxdg_output_v1 *xdg_output; +#endif + } RGFW_monitorNode; + + typedef struct RGFW_monitorList { + RGFW_monitorNode* head; + RGFW_monitorNode* cur; + } RGFW_monitorList; + + typedef struct RGFW_monitors { + RGFW_monitorList list; + RGFW_monitorList freeList; + size_t count; + RGFW_monitorNode data[RGFW_MAX_MONITORS]; + } RGFW_monitors; + + RGFWDEF RGFW_monitorNode* RGFW_monitors_add(RGFW_monitor mon); + RGFWDEF void RGFW_monitors_remove(RGFW_monitorNode* node, RGFW_monitorNode* prev); +#endif + +struct RGFW_info { + RGFW_window* root; + i32 windowCount; + + RGFW_mouse* hiddenMouse; + + RGFW_event events[RGFW_MAX_EVENTS]; /* A circular buffer (FIFO), using eventBottom/Len */ + + i32 eventBottom; + i32 eventLen; + RGFW_bool queueEvents; + RGFW_bool polledEvents; + + u32 apiKeycodes[RGFW_keyLast]; + #if defined(RGFW_X11) || defined(RGFW_WAYLAND) + u8 keycodes[256]; + #elif defined(RGFW_WINDOWS) + u8 keycodes[512]; + #elif defined(RGFW_MACOS) + u8 keycodes[128]; + #elif defined(RGFW_WASM) + u8 keycodes[256]; + #endif + + const char* className; + RGFW_bool useWaylandBool; + RGFW_bool stopCheckEvents_bool ; + u64 timerOffset; + + char* clipboard_data; + char* clipboard; /* for writing to the clipboard selection */ + size_t clipboard_len; + char filesSrc[RGFW_MAX_PATH * RGFW_MAX_DROPS]; + char** files; + #ifdef RGFW_X11 + Display* display; + XContext context; + Window helperWindow; + const char* instName; + XErrorEvent* x11Error; + #endif + #ifdef RGFW_WAYLAND + struct wl_display* wl_display; + struct xkb_context *xkb_context; + struct xkb_keymap *keymap; + struct xkb_state *xkb_state; + struct zxdg_decoration_manager_v1 *decoration_manager; + struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; + struct zwp_relative_pointer_v1 *relative_pointer; + struct zwp_pointer_constraints_v1 *constraint_manager; + struct xdg_toplevel_icon_manager_v1 *icon_manager; + + struct zxdg_output_manager_v1 *xdg_output_manager; + + struct wl_data_device_manager *data_device_manager; + struct wl_data_device *data_device; // supports clipboard and DND + + struct wl_keyboard* wl_keyboard; + struct wl_pointer* wl_pointer; + struct wl_compositor* compositor; + struct xdg_wm_base* xdg_wm_base; + struct wl_shm* shm; + struct wl_seat *seat; + struct wl_registry *registry; + u32 mouse_enter_serial; + struct wl_cursor_theme* wl_cursor_theme; + struct wl_surface* cursor_surface; + + RGFW_window* kbOwner; + RGFW_window* mouseOwner; /* what window has access to the mouse */ + + #endif + + RGFW_monitors monitors; + + #ifdef RGFW_UNIX + int eventWait_forceStop[3]; + i32 clock; + #endif + + #ifdef RGFW_MACOS + void* NSApp; + i64 flash; + void* customViewClasses[2]; /* NSView and NSOpenGLView */ + void* customWindowDelegateClass; + #endif + + #ifdef RGFW_OPENGL + RGFW_window* current; + #endif + #ifdef RGFW_EGL + void* EGL_display; + #endif + + RGFW_bool rawMouse; /* global raw mouse toggle */ + + RGFW_windowState windowState; /*! for checking window state events */ + + RGFW_keyState mouseButtons[RGFW_mouseFinal]; + RGFW_keyState keyboard[RGFW_keyLast]; + float scrollX, scrollY; + float vectorX, vectorY; +}; +#endif /* RGFW_NATIVE_HEADER */ + +#ifdef RGFW_IMPLEMENTATION + +/* global private API */ + +/* for C++ / C89 */ +RGFWDEF RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win); +RGFWDEF void RGFW_window_closePlatform(RGFW_window* win); + +RGFWDEF void RGFW_window_setFlagsInternal(RGFW_window* win, RGFW_windowFlags flags, RGFW_windowFlags cmpFlags); + +RGFWDEF void RGFW_initKeycodes(void); +RGFWDEF void RGFW_initKeycodesPlatform(void); +RGFWDEF void RGFW_resetPrevState(void); +RGFWDEF void RGFW_resetKey(void); +RGFWDEF void RGFW_unloadEGL(void); +RGFWDEF void RGFW_updateKeyModsEx(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll); +RGFWDEF void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll); +RGFWDEF void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show); +RGFWDEF void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value); + +RGFWDEF void RGFW_windowMaximizedCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h); +RGFWDEF void RGFW_windowMinimizedCallback(RGFW_window* win); +RGFWDEF void RGFW_windowRestoredCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h); +RGFWDEF void RGFW_windowMovedCallback(RGFW_window* win, i32 x, i32 y); +RGFWDEF void RGFW_windowResizedCallback(RGFW_window* win, i32 w, i32 h); +RGFWDEF void RGFW_windowQuitCallback(RGFW_window* win); +RGFWDEF void RGFW_mousePosCallback(RGFW_window* win, i32 x, i32 y, float vecX, float vecY); +RGFWDEF void RGFW_windowRefreshCallback(RGFW_window* win); +RGFWDEF void RGFW_focusCallback(RGFW_window* win, RGFW_bool inFocus); +RGFWDEF void RGFW_mouseNotifyCallback(RGFW_window* win, i32 x, i32 y, RGFW_bool status); +RGFWDEF void RGFW_dataDropCallback(RGFW_window* win, char** files, size_t count); +RGFWDEF void RGFW_dataDragCallback(RGFW_window* win, i32 x, i32 y); +RGFWDEF void RGFW_keyCallback(RGFW_window* win, RGFW_key key, RGFW_key sym, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool press); +RGFWDEF void RGFW_mouseButtonCallback(RGFW_window* win, RGFW_mouseButton button, RGFW_bool press); +RGFWDEF void RGFW_mouseScrollCallback(RGFW_window* win, float x, float y); +RGFWDEF void RGFW_scaleUpdatedCallback(RGFW_window* win, float scaleX, float scaleY); + +RGFWDEF void RGFW_setBit(u32* var, u32 mask, RGFW_bool set); +RGFWDEF void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode); + +RGFWDEF void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state); +RGFWDEF void RGFW_window_setRawMouseModePlatform(RGFW_window *win, RGFW_bool state); + +RGFWDEF void RGFW_copyImageData64(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, + u8* src_data, RGFW_format src_format, RGFW_bool is64bit); + +RGFWDEF RGFW_bool RGFW_loadEGL(void); + +#ifdef RGFW_OPENGL +typedef struct RGFW_attribStack { + i32* attribs; + size_t count; + size_t max; +} RGFW_attribStack; +RGFWDEF void RGFW_attribStack_init(RGFW_attribStack* stack, i32* attribs, size_t max); +RGFWDEF void RGFW_attribStack_pushAttrib(RGFW_attribStack* stack, i32 attrib); +RGFWDEF void RGFW_attribStack_pushAttribs(RGFW_attribStack* stack, i32 attrib1, i32 attrib2); + +RGFWDEF RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len); +#endif + +typedef struct RGFW_colorLayout { i32 r, g, b, a; } RGFW_colorLayout; + +#ifdef RGFW_X11 +RGFWDEF void RGFW_XCreateWindow (XVisualInfo visual, const char* name, RGFW_windowFlags flags, RGFW_window* win); +#endif +#ifdef RGFW_MACOS +RGFWDEF void RGFW_osx_initView(RGFW_window* win); +#endif +/* end of global private API defs */ + +RGFW_info* _RGFW = NULL; +void RGFW_setInfo(RGFW_info* info) { _RGFW = info; } +RGFW_info* RGFW_getInfo(void) { return _RGFW; } + + +void* RGFW_alloc(size_t size) { return RGFW_ALLOC(size); } +void RGFW_free(void* ptr) { RGFW_FREE(ptr); } + +void RGFW_useWayland(RGFW_bool wayland) { RGFW_init(); _RGFW->useWaylandBool = RGFW_BOOL(wayland); } +RGFW_bool RGFW_usingWayland(void) { return _RGFW->useWaylandBool; } + +void RGFW_setRawMouseMode(RGFW_bool state) { + _RGFW->rawMouse = state; + RGFW_window_setRawMouseModePlatform(_RGFW->root, state); +} + +void RGFW_clipboard_switch(char* newstr); +void RGFW_clipboard_switch(char* newstr) { + if (_RGFW->clipboard_data != NULL) + RGFW_FREE(_RGFW->clipboard_data); + _RGFW->clipboard_data = newstr; +} + +#define RGFW_CHECK_CLIPBOARD() \ + if (size <= 0 && _RGFW->clipboard_data != NULL) \ + return (const char*)_RGFW->clipboard_data; \ + else if (size <= 0) \ + return "\0"; + +const char* RGFW_readClipboard(size_t* len) { + RGFW_ssize_t size = RGFW_readClipboardPtr(NULL, 0); + RGFW_CHECK_CLIPBOARD(); + char* str = (char*)RGFW_ALLOC((size_t)size); + RGFW_ASSERT(str != NULL); + str[0] = '\0'; + + size = RGFW_readClipboardPtr(str, (size_t)size); + + RGFW_CHECK_CLIPBOARD(); + + if (len != NULL) *len = (size_t)size; + + RGFW_clipboard_switch(str); + return (const char*)str; +} + +/* +RGFW_IMPLEMENTATION starts with generic RGFW defines + +This is the start of keycode data +*/ + + + +void RGFW_initKeycodes(void) { + RGFW_MEMSET(_RGFW->keycodes, 0, sizeof(_RGFW->keycodes)); + RGFW_initKeycodesPlatform(); + u32 i, y; + for (i = 0; i < RGFW_keyLast; i++) { + for (y = 0; y < sizeof(_RGFW->keycodes); y++) { + if (_RGFW->keycodes[y] == i) { + _RGFW->apiKeycodes[i] = y; + break; + } + } + } + + + RGFW_resetKey(); +} + +u32 RGFW_apiKeyToRGFW(u32 keycode) { + /* make sure the key isn't out of bounds */ + if (keycode > sizeof(_RGFW->keycodes) / sizeof(u8)) + return 0; + + return _RGFW->keycodes[keycode]; +} + +u32 RGFW_rgfwToApiKey(u32 keycode) { + /* make sure the key isn't out of bounds */ + if (keycode > sizeof(_RGFW->apiKeycodes) / sizeof(u32)) + return 0; + + return _RGFW->apiKeycodes[keycode]; +} + +void RGFW_resetKey(void) { RGFW_MEMSET(_RGFW->keyboard, 0, sizeof(_RGFW->keyboard)); } +/* + this is the end of keycode data +*/ + +/* + event callback defines start here +*/ + + +/* + These exist to avoid the + if (func == NULL) check + for (allegedly) better performance + + RGFW_EMPTY_DEF exists to prevent the missing-prototypes warning +*/ +#define RGFW_CALLBACK_DEFINE(x, x2) \ +RGFW_##x##func RGFW_##x##CallbackSrc = NULL; \ +RGFW_##x##func RGFW_set##x2##Callback(RGFW_##x##func func) { \ + RGFW_##x##func prev = RGFW_##x##CallbackSrc; \ + RGFW_##x##CallbackSrc = func; \ + return prev; \ +} + +RGFW_CALLBACK_DEFINE(windowMaximized, WindowMaximized) +RGFW_CALLBACK_DEFINE(windowMinimized, WindowMinimized) +RGFW_CALLBACK_DEFINE(windowRestored, WindowRestored) +RGFW_CALLBACK_DEFINE(windowMoved, WindowMoved) +RGFW_CALLBACK_DEFINE(windowResized, WindowResized) +RGFW_CALLBACK_DEFINE(windowQuit, WindowQuit) +RGFW_CALLBACK_DEFINE(mousePos, MousePos) +RGFW_CALLBACK_DEFINE(windowRefresh, WindowRefresh) +RGFW_CALLBACK_DEFINE(focus, Focus) +RGFW_CALLBACK_DEFINE(mouseNotify, MouseNotify) +RGFW_CALLBACK_DEFINE(dataDrop, DataDrop) +RGFW_CALLBACK_DEFINE(dataDrag, DataDrag) +RGFW_CALLBACK_DEFINE(key, Key) +RGFW_CALLBACK_DEFINE(mouseButton, MouseButton) +RGFW_CALLBACK_DEFINE(mouseScroll, MouseScroll) +RGFW_CALLBACK_DEFINE(scaleUpdated, ScaleUpdated) +RGFW_CALLBACK_DEFINE(debug, Debug) +#define RGFW_debugCallback(type, err, msg) if (RGFW_debugCallbackSrc) RGFW_debugCallbackSrc(type, err, msg); +#undef RGFW_CALLBACK_DEFINE + +void RGFW_windowMaximizedCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h) { + win->internal.flags |= RGFW_windowMaximize; + + if (!(win->internal.enabledEvents & RGFW_windowMaximizedFlag)) return; + + RGFW_event event; + event.type = RGFW_windowMaximized; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowMaximizedCallbackSrc) RGFW_windowMaximizedCallbackSrc(win, x, y, w, h); +} + +void RGFW_windowMinimizedCallback(RGFW_window* win) { + win->internal.flags |= RGFW_windowMinimize; + + if (!(win->internal.enabledEvents & RGFW_windowMinimizedFlag)) return; + + RGFW_event event; + event.type = RGFW_windowMinimized; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowMinimizedCallbackSrc) RGFW_windowMinimizedCallbackSrc(win); +} + +void RGFW_windowRestoredCallback(RGFW_window* win, i32 x, i32 y, i32 w, i32 h) { + win->internal.flags &= ~(u32)RGFW_windowMinimize; + if (RGFW_window_isMaximized(win) == RGFW_FALSE) win->internal.flags &= ~(u32)RGFW_windowMaximize; + + if (!(win->internal.enabledEvents & RGFW_windowRestoredFlag)) return; + + RGFW_event event; + event.type = RGFW_windowRestored; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowRestoredCallbackSrc) RGFW_windowRestoredCallbackSrc(win, x, y, w, h); +} + +void RGFW_windowMovedCallback(RGFW_window* win, i32 x, i32 y) { + win->x = x; + win->y = y; + if (!(win->internal.enabledEvents & RGFW_windowMovedFlag)) return; + + RGFW_event event; + event.type = RGFW_windowMoved; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowMovedCallbackSrc) RGFW_windowMovedCallbackSrc(win, x, y); +} + +void RGFW_windowResizedCallback(RGFW_window* win, i32 w, i32 h) { + win->w = w; + win->h = h; + + if (!(win->internal.enabledEvents & RGFW_windowResizedFlag)) return; + RGFW_event event; + event.type = RGFW_windowResized; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowResizedCallbackSrc) RGFW_windowResizedCallbackSrc(win, w, h); +} + +void RGFW_windowQuitCallback(RGFW_window* win) { + win->internal.shouldClose = RGFW_TRUE; + + RGFW_event event; + event.type = RGFW_quit; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowQuitCallbackSrc) RGFW_windowQuitCallbackSrc(win); +} + +void RGFW_mousePosCallback(RGFW_window* win, i32 x, i32 y, float vecX, float vecY) { + win->internal.lastMouseX = x; + win->internal.lastMouseY = y; + _RGFW->vectorX = vecX; + _RGFW->vectorY = vecY; + + if (!(win->internal.enabledEvents & RGFW_mousePosChangedFlag)) return; + + RGFW_event event; + event.type = RGFW_mousePosChanged; + event.mouse.x = x; + event.mouse.y = y; + event.mouse.vecX = vecX; + event.mouse.vecY = vecY; + event.common.win = win; + + RGFW_eventQueuePush(&event); + + if (RGFW_mousePosCallbackSrc) RGFW_mousePosCallbackSrc(win, x, y, vecX, vecY); +} + +void RGFW_windowRefreshCallback(RGFW_window* win) { + if (!(win->internal.enabledEvents & RGFW_windowRefreshFlag)) return; + RGFW_event event; + event.type = RGFW_windowRefresh; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_windowRefreshCallbackSrc) RGFW_windowRefreshCallbackSrc(win); +} + +void RGFW_focusCallback(RGFW_window* win, RGFW_bool inFocus) { + win->internal.inFocus = inFocus; + + if (win->internal.captureMouse) { + RGFW_window_captureMousePlatform(win, inFocus); + } + + RGFW_event event; + event.common.win = win; + + if (inFocus == RGFW_TRUE) { + if ((win->internal.flags & RGFW_windowFullscreen)) + RGFW_window_raise(win); + + event.type = RGFW_focusIn; + } else if (inFocus == RGFW_FALSE) { + if ((win->internal.flags & RGFW_windowFullscreen)) + RGFW_window_minimize(win); + + size_t key; + for (key = 0; key < RGFW_keyLast; key++) { + if (RGFW_isKeyDown((u8)key) == RGFW_FALSE) continue; + + _RGFW->keyboard[key].current = RGFW_FALSE; + u8 sym = RGFW_rgfwToKeyChar((u32)key); + + if ((win->internal.enabledEvents & RGFW_BIT(RGFW_keyReleased))) { + RGFW_keyCallback(win, (u8)key, sym, win->internal.mod, RGFW_FALSE, RGFW_FALSE); + } + } + + RGFW_resetKey(); + event.type = RGFW_focusOut; + } + + event.common.win = win; + + RGFW_eventQueuePush(&event); + + if (RGFW_focusCallbackSrc) RGFW_focusCallbackSrc(win, inFocus); +} + +void RGFW_mouseNotifyCallback(RGFW_window* win, i32 x, i32 y, RGFW_bool status) { + win->internal.mouseInside = status; + _RGFW->windowState.win = win; + + win->internal.lastMouseX = x; + win->internal.lastMouseY = y; + + RGFW_event event; + event.common.win = win; + event.mouse.x = x; + event.mouse.y = y; + + if (status) { + if (!(win->internal.enabledEvents & RGFW_mouseEnterFlag)) return; + _RGFW->windowState.mouseEnter = RGFW_TRUE; + _RGFW->windowState.win = win; + event.type = RGFW_mouseEnter; + } else { + if (!(win->internal.enabledEvents & RGFW_mouseLeaveFlag)) return; + _RGFW->windowState.winLeave = win; + _RGFW->windowState.mouseLeave = RGFW_TRUE; + event.type = RGFW_mouseLeave; + } + + RGFW_eventQueuePush(&event); + + if (RGFW_mouseNotifyCallbackSrc) RGFW_mouseNotifyCallbackSrc(win, x, y, status); +} + +void RGFW_dataDropCallback(RGFW_window* win, char** files, size_t count) { + if (!(win->internal.enabledEvents & RGFW_dataDropFlag) || !(win->internal.flags & RGFW_windowAllowDND)) + return; + + _RGFW->windowState.win = win; + _RGFW->windowState.dataDrop = RGFW_TRUE; + _RGFW->windowState.filesCount = count; + + RGFW_event event; + event.type = RGFW_dataDrop; + event.drop.files = files; + event.drop.count = count; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_dataDropCallbackSrc) RGFW_dataDropCallbackSrc(win, files, count); +} + +void RGFW_dataDragCallback(RGFW_window* win, i32 x, i32 y) { + _RGFW->windowState.win = win; + _RGFW->windowState.dataDragging = RGFW_TRUE; + _RGFW->windowState.dropX = x; + _RGFW->windowState.dropY = y; + + if (win->internal.enabledEvents & RGFW_dataDragFlag) return; + + RGFW_event event; + event.type = RGFW_dataDrag; + event.drag.x = x; + event.drag.y = y; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_dataDragCallbackSrc) RGFW_dataDragCallbackSrc(win, x, y); +} + +void RGFW_keyCallback(RGFW_window* win, RGFW_key key, RGFW_key sym, RGFW_keymod mod, RGFW_bool repeat, RGFW_bool press) { + RGFW_event event; + + if (press) { + if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return; + event.type = RGFW_keyPressed; + } else { + if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return; + event.type = RGFW_keyReleased; + } + + event.key.value = key; + event.key.sym = sym; + event.key.mod = repeat; + event.key.mod = mod; + event.common.win = win; + RGFW_eventQueuePush(&event); + + _RGFW->keyboard[event.key.value].prev = _RGFW->keyboard[event.key.value].current; + _RGFW->keyboard[event.key.value].current = press; + + if (RGFW_keyCallbackSrc) RGFW_keyCallbackSrc(win, key, sym, mod, repeat, press); +} + +void RGFW_mouseButtonCallback(RGFW_window* win, RGFW_mouseButton button, RGFW_bool press) { + RGFW_event event; + if (press) { + if (!(win->internal.enabledEvents & RGFW_mouseButtonPressedFlag)) return; + event.type = RGFW_mouseButtonPressed; + } else { + if (!(win->internal.enabledEvents & RGFW_mouseButtonReleasedFlag)) return; + event.type = RGFW_mouseButtonReleased; + } + + _RGFW->mouseButtons[button].prev = _RGFW->mouseButtons[button].current; + _RGFW->mouseButtons[button].current = press; + + event.button.value = button; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_mouseButtonCallbackSrc) RGFW_mouseButtonCallbackSrc(win, button, press); +} + +void RGFW_mouseScrollCallback(RGFW_window* win, float x, float y) { + if (!(win->internal.enabledEvents & RGFW_mouseScrollFlag)) return; + _RGFW->scrollX = x; + _RGFW->scrollY = y; + + RGFW_event event; + event.type = RGFW_mouseScroll; + event.scroll.x = x; + event.scroll.y = y; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_mouseScrollCallbackSrc) RGFW_mouseScrollCallbackSrc(win, x, y); +} + +void RGFW_scaleUpdatedCallback(RGFW_window* win, float scaleX, float scaleY) { + if (win->internal.flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win); + if (!(win->internal.enabledEvents & RGFW_scaleUpdatedFlag)) return; + + RGFW_event event; + event.type = RGFW_scaleUpdated; + event.scale.x = scaleX; + event.scale.y = scaleY; + event.common.win = win; + RGFW_eventQueuePush(&event); + + if (RGFW_scaleUpdatedCallbackSrc) RGFW_scaleUpdatedCallbackSrc(win, scaleX, scaleY); +} + +#ifdef RGFW_DEBUG +#include +#endif + +void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, const char* msg) { + RGFW_debugCallback(type, err, msg); + + #ifdef RGFW_DEBUG + switch (type) { + case RGFW_typeInfo: RGFW_PRINTF("RGFW INFO (%i %i): %s", type, err, msg); break; + case RGFW_typeError: RGFW_PRINTF("RGFW DEBUG (%i %i): %s", type, err, msg); break; + case RGFW_typeWarning: RGFW_PRINTF("RGFW WARNING (%i %i): %s", type, err, msg); break; + default: break; + } + + RGFW_PRINTF("\n"); + #endif +} + +void RGFW_window_checkMode(RGFW_window* win); +void RGFW_window_checkMode(RGFW_window* win) { + if (RGFW_window_isMinimized(win) && (win->internal.enabledEvents & RGFW_windowMinimizedFlag)) { + RGFW_windowMinimizedCallback(win); + } else if (RGFW_window_isMaximized(win) && (win->internal.enabledEvents & RGFW_windowMaximizedFlag)) { + RGFW_windowMaximizedCallback(win, win->x, win->y, win->w, win->h); + } else if ((((win->internal.flags & RGFW_windowMinimize) && !RGFW_window_isMaximized(win)) || + (win->internal.flags & RGFW_windowMaximize && !RGFW_window_isMaximized(win))) && (win->internal.enabledEvents & RGFW_windowRestoredFlag)) { + RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h); + } +} + +/* +no more event call back defines +*/ + +size_t RGFW_sizeofInfo(void) { return sizeof(RGFW_info); } +size_t RGFW_sizeofNativeImage(void) { return sizeof(RGFW_nativeImage); } +size_t RGFW_sizeofSurface(void) { return sizeof(RGFW_surface); } +size_t RGFW_sizeofWindow(void) { return sizeof(RGFW_window); } +size_t RGFW_sizeofWindowSrc(void) { return sizeof(RGFW_window_src); } + +RGFW_window_src* RGFW_window_getSrc(RGFW_window* win) { return &win->src; } +RGFW_bool RGFW_window_getPosition(RGFW_window* win, i32* x, i32* y) { if (x) *x = win->x; if (y) *y = win->y; return RGFW_TRUE; } +RGFW_bool RGFW_window_getSize(RGFW_window* win, i32* w, i32* h) { if (w) *w = win->w; if (h) *h = win->h; return RGFW_TRUE; } +u32 RGFW_window_getFlags(RGFW_window* win) { return win->internal.flags; } +RGFW_key RGFW_window_getExitKey(RGFW_window* win) { return win->internal.exitKey; } +void RGFW_window_setExitKey(RGFW_window* win, RGFW_key key) { win->internal.exitKey = key; } +void RGFW_window_setEnabledEvents(RGFW_window* win, RGFW_eventFlag events) { win->internal.enabledEvents = events; } +RGFW_eventFlag RGFW_window_getEnabledEvents(RGFW_window* win) { return win->internal.enabledEvents; } +void RGFW_window_setDisabledEvents(RGFW_window* win, RGFW_eventFlag events) { RGFW_window_setEnabledEvents(win, (RGFW_allEventFlags) & ~(u32)events); } +void RGFW_window_setEventState(RGFW_window* win, RGFW_eventFlag event, RGFW_bool state) { RGFW_setBit(&win->internal.enabledEvents, event, state); } +void* RGFW_window_getUserPtr(RGFW_window* win) { return win->userPtr; } +void RGFW_window_setUserPtr(RGFW_window* win, void* ptr) { win->userPtr = ptr; } + +RGFW_bool RGFW_window_getSizeInPixels(RGFW_window* win, i32* w, i32* h) { + RGFW_monitor mon = RGFW_window_getMonitor(win); + + if (w) *w = (i32)((float)win->w * mon.pixelRatio); + if (h) *h = (i32)((float)win->h * mon.pixelRatio); + + return RGFW_TRUE; +} + + +#if defined(RGFW_USE_XDL) && defined(RGFW_X11) + #define XDL_IMPLEMENTATION + #include "XDL.h" +#endif + +#ifndef RGFW_FORCE_INIT +RGFW_info _rgfwGlobal; +#endif + +i32 RGFW_init(void) { return RGFW_init_ptr(&_rgfwGlobal); } +void RGFW_deinit(void) { RGFW_deinit_ptr(&_rgfwGlobal); } + +i32 RGFW_initPlatform(void); +void RGFW_deinitPlatform(void); + +i32 RGFW_init_ptr(RGFW_info* info) { + if (info == _RGFW || info == NULL) return 1; + + RGFW_setInfo(info); + RGFW_MEMSET(_RGFW, 0, sizeof(RGFW_info)); + _RGFW->queueEvents = RGFW_FALSE; + _RGFW->polledEvents = RGFW_FALSE; +#ifdef RGFW_WAYLAND + _RGFW->useWaylandBool = RGFW_TRUE; +#endif + + _RGFW->files = (char**)(void*)_RGFW->filesSrc; + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + _RGFW->files[i] = (char*)(_RGFW->filesSrc + RGFW_MAX_DROPS + (i * RGFW_MAX_PATH)); + + _RGFW->monitors.freeList.head = &_RGFW->monitors.data[0]; + _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.head; + + for (i = 1; i < RGFW_MAX_MONITORS; i++) { + RGFW_monitorNode* newNode = &_RGFW->monitors.data[i]; + _RGFW->monitors.freeList.cur->next = newNode; + _RGFW->monitors.freeList.cur = _RGFW->monitors.freeList.cur->next; + } + + RGFW_initKeycodes(); + i32 out = RGFW_initPlatform(); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, "global context initialized"); + + return out; +} + +#ifndef RGFW_EGL +void RGFW_unloadEGL(void) { } +#endif + +void RGFW_deinit_ptr(RGFW_info* info) { + if (info == NULL) return; + + RGFW_setInfo(info); + RGFW_unloadEGL(); + RGFW_deinitPlatform(); + + _RGFW->root = NULL; + _RGFW->windowCount = 0; + RGFW_setInfo(NULL); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, "global context deinitialized"); +} + +RGFW_window* RGFW_createWindow(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags) { + RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window)); + RGFW_ASSERT(win != NULL); + return RGFW_createWindowPtr(name, x, y, w, h, flags, win); +} + +void RGFW_window_close(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_window_closePtr(win); + RGFW_FREE(win); +} + +RGFW_window* RGFW_createWindowPtr(const char* name, i32 x, i32 y, i32 w, i32 h, RGFW_windowFlags flags, RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (name == NULL) name = "\0"; + + RGFW_MEMSET(win, 0, sizeof(RGFW_window)); + + if (_RGFW == NULL) RGFW_init(); + _RGFW->windowCount++; + + /* rect based the requested flags */ + if (_RGFW->root == NULL) { + RGFW_setRootWindow(win); + } + + /* set and init the new window's data */ + win->x = x; + win->y = y; + win->w = w; + win->h = h; + win->internal.flags = flags; + win->internal.enabledEvents = RGFW_allEventFlags; + + RGFW_window* ret = RGFW_createWindowPlatform(name, flags, win); + +#ifndef RGFW_X11 + RGFW_window_setFlagsInternal(win, flags, 0); +#endif + +#ifdef RGFW_OPENGL + win->src.gfxType = 0; + if (flags & RGFW_windowOpenGL) + RGFW_window_createContext_OpenGL(win, RGFW_getGlobalHints_OpenGL()); +#endif + +#ifdef RGFW_EGL + if (flags & RGFW_windowEGL) + RGFW_window_createContext_EGL(win, RGFW_getGlobalHints_OpenGL()); +#endif + + /* X11 creates the window after the OpenGL context is created (because of visual garbage), + * so we have to wait to set the flags + * This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used + * if a window is created, CreateContext will delete the window and create a new one + * */ +#ifdef RGFW_X11 + RGFW_window_setFlagsInternal(win, flags, 0); +#endif + +#ifdef RGFW_MACOS + /*NOTE: another OpenGL/setFlags related hack, this because OSX the 'view' class must be setup after the NSOpenGL view is made AND after setFlags happens */ + RGFW_osx_initView(win); +#endif + +#ifdef RGFW_WAYLAND + /* recieve all events needed to configure the surface */ + /* also gets the wl_outputs */ + if (RGFW_usingWayland()) { + wl_display_roundtrip(_RGFW->wl_display); + /* NOTE: this is a hack so that way wayland spawns a window, even if nothing is drawn */ + if (!(flags & RGFW_windowOpenGL) && !(flags & RGFW_windowEGL)) { + u8* data = (u8*)RGFW_ALLOC((u32)(win->w * win->h * 3)); + RGFW_MEMSET(data, 0, (u32)(win->w * win->h * 3) * sizeof(u8)); + RGFW_surface* surface = RGFW_createSurface(data, win->w, win->h, RGFW_formatBGR8); + RGFW_window_blitSurface(win, surface); + RGFW_FREE(data); + RGFW_surface_free(surface); + } + } +#endif + + if (!(flags & RGFW_windowHideMouse)) { + RGFW_window_setMouseDefault(win); + } + + RGFW_window_setName(win, name); + if (!(flags & RGFW_windowHide)) { + flags |= RGFW_windowHide; + RGFW_window_show(win); + } + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, "a new window was created"); + + return ret; +} + +void RGFW_window_closePtr(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + if (win->internal.captureMouse) { + RGFW_window_unholdMouse(win); + } + + #ifdef RGFW_EGL + if ((win->src.gfxType & RGFW_gfxEGL) && win->src.ctx.egl) { + RGFW_window_deleteContext_EGL(win, win->src.ctx.egl); + win->src.ctx.egl = NULL; + } + #endif + + #ifdef RGFW_OPENGL + if ((win->src.gfxType & RGFW_gfxNativeOpenGL) && win->src.ctx.native) { + RGFW_window_deleteContext_OpenGL(win, win->src.ctx.native); + win->src.ctx.native = NULL; + } + #endif + + RGFW_window_closePlatform(win); + + RGFW_clipboard_switch(NULL); + _RGFW->windowCount--; + if (_RGFW->windowCount == 0) RGFW_deinit(); + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, "a window was freed"); +} + +void RGFW_setQueueEvents(RGFW_bool queue) { _RGFW->queueEvents = RGFW_BOOL(queue); } + +void RGFW_eventQueueFlush(void) { _RGFW->eventLen = 0; } + +void RGFW_eventQueuePush(const RGFW_event* event) { + if (_RGFW->queueEvents == RGFW_FALSE) return; + RGFW_ASSERT(_RGFW->eventLen >= 0); + + if (_RGFW->eventLen >= RGFW_MAX_EVENTS) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEventQueue, "Event queue limit 'RGFW_MAX_EVENTS' has been reached automatically flushing queue."); + RGFW_eventQueueFlush(); + return; + } + + i32 eventTop = (_RGFW->eventBottom + _RGFW->eventLen) % RGFW_MAX_EVENTS; + _RGFW->eventLen += 1; + _RGFW->events[eventTop] = *event; +} + +RGFW_event* RGFW_eventQueuePop(void) { + RGFW_ASSERT(_RGFW->eventLen >= 0 && _RGFW->eventLen <= RGFW_MAX_EVENTS); + RGFW_event* ev; + + if (_RGFW->eventLen == 0) { + return NULL; + } + + ev = &_RGFW->events[_RGFW->eventBottom]; + _RGFW->eventLen -= 1; + _RGFW->eventBottom = (_RGFW->eventBottom + 1) % RGFW_MAX_EVENTS; + + return ev; +} + +RGFW_bool RGFW_checkEvent(RGFW_event* event) { + if (_RGFW->eventLen == 0 && _RGFW->polledEvents == RGFW_FALSE) { + _RGFW->queueEvents = RGFW_TRUE; + RGFW_pollEvents(); + _RGFW->polledEvents = RGFW_TRUE; + } + + if (RGFW_checkQueuedEvent(event) == RGFW_FALSE) { + _RGFW->polledEvents = RGFW_FALSE; + return RGFW_FALSE; + } + + return RGFW_TRUE; +} + +RGFW_bool RGFW_checkQueuedEvent(RGFW_event* event) { + RGFW_event* ev; + _RGFW->queueEvents = RGFW_TRUE; + /* check queued events */ + ev = RGFW_eventQueuePop(); + if (ev != NULL) { + *event = *ev; + return RGFW_TRUE; + } + + return RGFW_FALSE; +} + +void RGFW_resetPrevState(void) { + size_t i; /*!< reset each previous state */ + for (i = 0; i < RGFW_keyLast; i++) _RGFW->keyboard[i].prev = _RGFW->keyboard[i].current; + for (i = 0; i < RGFW_mouseFinal; i++) _RGFW->mouseButtons[i].prev = _RGFW->mouseButtons[i].current; + _RGFW->scrollX = 0.0f; + _RGFW->scrollY = 0.0f; + _RGFW->vectorX = (float)0.0f; + _RGFW->vectorY = (float)0.0f; + RGFW_MEMSET(&_RGFW->windowState, 0, sizeof(_RGFW->windowState)); +} + +RGFW_bool RGFW_isKeyPressed(RGFW_key key) { + return _RGFW != NULL && _RGFW->keyboard[key].current && !_RGFW->keyboard[key].prev; +} + +RGFW_bool RGFW_isKeyDown(RGFW_key key) { + return _RGFW != NULL && _RGFW->keyboard[key].current; +} + +RGFW_bool RGFW_isKeyReleased(RGFW_key key) { + return _RGFW != NULL && !_RGFW->keyboard[key].current && _RGFW->keyboard[key].prev; +} + + +RGFW_bool RGFW_isMousePressed(RGFW_mouseButton button) { + return _RGFW != NULL && _RGFW->mouseButtons[button].current && !_RGFW->mouseButtons[button].prev; +} +RGFW_bool RGFW_isMouseDown(RGFW_mouseButton button) { + return _RGFW != NULL && _RGFW->mouseButtons[button].current; +} +RGFW_bool RGFW_isMouseReleased(RGFW_mouseButton button) { + return _RGFW != NULL && !_RGFW->mouseButtons[button].current && _RGFW->mouseButtons[button].prev; +} + +void RGFW_getMouseScroll(float* x, float* y) { + RGFW_ASSERT(_RGFW != NULL); + if (x) *x = _RGFW->scrollX; + if (y) *y = _RGFW->scrollY; +} + +void RGFW_getMouseVector(float* x, float* y) { + RGFW_ASSERT(_RGFW != NULL); + if (x) *x = _RGFW->vectorX; + if (y) *y = _RGFW->vectorY; +} + +RGFW_bool RGFW_window_didMouseLeave(RGFW_window* win) { return _RGFW->windowState.winLeave == win && _RGFW->windowState.mouseLeave; } +RGFW_bool RGFW_window_didMouseEnter(RGFW_window* win) { return _RGFW->windowState.win == win && _RGFW->windowState.mouseEnter; } +RGFW_bool RGFW_window_isMouseInside(RGFW_window* win) { return win->internal.mouseInside; } + +RGFW_bool RGFW_window_isDataDragging(RGFW_window* win) { return RGFW_window_getDataDrag(win, (i32*)NULL, (i32*)NULL); } +RGFW_bool RGFW_window_didDataDrop(RGFW_window* win) { return RGFW_window_getDataDrop(win, (const char***)NULL, (size_t*)NULL);} + + +RGFW_bool RGFW_window_getDataDrag(RGFW_window* win, i32* x, i32* y) { + if (_RGFW->windowState.win != win || _RGFW->windowState.dataDragging == RGFW_FALSE) return RGFW_FALSE; + if (x) *x = _RGFW->windowState.dropX; + if (y) *y = _RGFW->windowState.dropY; + return RGFW_TRUE; +} +RGFW_bool RGFW_window_getDataDrop(RGFW_window* win, const char*** files, size_t* count) { + if (_RGFW->windowState.win != win || _RGFW->windowState.dataDrop == RGFW_FALSE) return RGFW_FALSE; + if (files) *files = (const char**)_RGFW->files; + if (count) *count = _RGFW->windowState.filesCount; + return RGFW_TRUE; +} + +RGFW_bool RGFW_window_checkEvent(RGFW_window* win, RGFW_event* event) { + if (_RGFW->eventLen == 0 && _RGFW->polledEvents == RGFW_FALSE) { + _RGFW->queueEvents = RGFW_TRUE; + RGFW_pollEvents(); + _RGFW->polledEvents = RGFW_TRUE; + } + + if (RGFW_window_checkQueuedEvent(win, event) == RGFW_FALSE) { + _RGFW->polledEvents = RGFW_FALSE; + return RGFW_FALSE; + } + + return RGFW_TRUE; +} + +RGFW_bool RGFW_window_checkQueuedEvent(RGFW_window* win, RGFW_event* event) { + RGFW_event* ev; + RGFW_ASSERT(win != NULL); + _RGFW->queueEvents = RGFW_TRUE; + /* check queued events */ + ev = RGFW_window_eventQueuePop(win); + if (ev == NULL) return RGFW_FALSE; + + *event = *ev; + return RGFW_TRUE; +} + +RGFW_event* RGFW_window_eventQueuePop(RGFW_window* win) { + RGFW_event* ev = RGFW_eventQueuePop(); + if (ev == NULL) return ev; + + for (i32 i = 1; i < _RGFW->eventLen && ev->common.win != win && ev->common.win != NULL; i++) { + RGFW_eventQueuePush(ev); + ev = RGFW_eventQueuePop(); + } + + if (ev->common.win != win && ev->common.win != NULL) { + return NULL; + } + + return ev; +} + +void RGFW_setRootWindow(RGFW_window* win) { _RGFW->root = win; } +RGFW_window* RGFW_getRootWindow(void) { return _RGFW->root; } + +#ifndef RGFW_EGL +RGFW_bool RGFW_loadEGL(void) { return RGFW_FALSE; } +#endif + +void RGFW_window_setFlagsInternal(RGFW_window* win, RGFW_windowFlags flags, RGFW_windowFlags cmpFlags) { + if (flags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 0); + else if (cmpFlags & RGFW_windowNoBorder) RGFW_window_setBorder(win, 1); +#ifndef RGFW_NO_MONITOR + if (flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win); +#endif + if (flags & RGFW_windowMaximize) RGFW_window_maximize(win); + else if (cmpFlags & RGFW_windowMaximize) RGFW_window_restore(win); + if (flags & RGFW_windowMinimize) RGFW_window_minimize(win); + else if (cmpFlags & RGFW_windowMinimize) RGFW_window_restore(win); + if (flags & RGFW_windowCenter) RGFW_window_center(win); + if (flags & RGFW_windowCenterCursor) RGFW_window_moveMouse(win, win->x + (win->w / 2), win->y + (win->h / 2)); + if (flags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, RGFW_TRUE); + else if (cmpFlags & RGFW_windowFullscreen) RGFW_window_setFullscreen(win, 0); + if (flags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 0); + else if (cmpFlags & RGFW_windowHideMouse) RGFW_window_showMouse(win, 1); + if (flags & RGFW_windowHide) RGFW_window_hide(win); + else if (cmpFlags & RGFW_windowHide) RGFW_window_show(win); + if (flags & RGFW_windowFloating) RGFW_window_setFloating(win, 1); + else if (cmpFlags & RGFW_windowFloating) RGFW_window_setFloating(win, 0); + if (flags & RGFW_windowRawMouse) RGFW_window_setRawMouseMode(win, RGFW_TRUE); + else if (cmpFlags & RGFW_windowRawMouse) RGFW_window_setRawMouseMode(win, RGFW_FALSE); + if (flags & RGFW_windowCaptureMouse) RGFW_window_captureRawMouse(win, RGFW_TRUE); + else if (cmpFlags & RGFW_windowCaptureMouse) RGFW_window_captureMouse(win, RGFW_FALSE); + if (flags & RGFW_windowFocus) RGFW_window_focus(win); + + if (flags & RGFW_windowNoResize) { + RGFW_window_setMaxSize(win, win->w, win->h); + RGFW_window_setMinSize(win, win->w, win->h); + } else if (cmpFlags & RGFW_windowNoResize) { + RGFW_window_setMaxSize(win, 0, 0); + RGFW_window_setMinSize(win, 0, 0); + } + + win->internal.flags = flags; +} + + +void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) { RGFW_window_setFlagsInternal(win, flags, win->internal.flags); } + +RGFW_bool RGFW_window_isInFocus(RGFW_window* win) { +#ifdef RGFW_WASM + return RGFW_TRUE; +#else + return RGFW_BOOL(win->internal.inFocus); +#endif +} + +void RGFW_setClassName(const char* name) { RGFW_init(); _RGFW->className = name; } + +#ifndef RGFW_X11 +void RGFW_setXInstName(const char* name) { RGFW_UNUSED(name); } +#endif + +RGFW_bool RGFW_window_getMouse(RGFW_window* win, i32* x, i32* y) { + RGFW_ASSERT(win != NULL); + if (x) *x = win->internal.lastMouseX; + if (y) *y = win->internal.lastMouseY; + return RGFW_TRUE; +} + +RGFW_bool RGFW_window_isKeyPressed(RGFW_window* win, RGFW_key key) { return RGFW_isKeyPressed(key) && RGFW_window_isInFocus(win); } +RGFW_bool RGFW_window_isKeyDown(RGFW_window* win, RGFW_key key) { return RGFW_isKeyDown(key) && RGFW_window_isInFocus(win); } +RGFW_bool RGFW_window_isKeyReleased(RGFW_window* win, RGFW_key key) { return RGFW_isKeyReleased(key) && RGFW_window_isInFocus(win); } + +RGFW_bool RGFW_window_isMousePressed(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMousePressed(button) && RGFW_window_isInFocus(win); } +RGFW_bool RGFW_window_isMouseDown(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMouseDown(button) && RGFW_window_isInFocus(win); } +RGFW_bool RGFW_window_isMouseReleased(RGFW_window* win, RGFW_mouseButton button) { return RGFW_isMouseReleased(button) && RGFW_window_isInFocus(win); } + + + +#ifndef RGFW_X11 +void* RGFW_getDisplay_X11(void) { return NULL; } +u64 RGFW_window_getWindow_X11(RGFW_window* win) { RGFW_UNUSED(win); return 0; } +#endif + +#ifndef RGFW_WAYLAND +struct wl_display* RGFW_getDisplay_Wayland(void) { return NULL; } +struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +#endif + +#ifndef RGFW_WINDOWS +void* RGFW_window_getHWND(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +void* RGFW_window_getHDC(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +#endif + +#ifndef RGFW_MACOS +void* RGFW_window_getView_OSX(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer) { RGFW_UNUSED(win); RGFW_UNUSED(layer); } +void* RGFW_getLayer_OSX(void) { return NULL; } +void* RGFW_window_getWindow_OSX(RGFW_window* win) { RGFW_UNUSED(win); return NULL; } +#endif + +void RGFW_setBit(u32* var, u32 mask, RGFW_bool set) { + if (set) *var |= mask; + else *var &= ~mask; +} + +void RGFW_window_center(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_monitor mon = RGFW_window_getMonitor(win); + RGFW_window_move(win, (i32)(mon.mode.w - win->w) / 2, (mon.mode.h - win->h) / 2); +} + +RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win) { + RGFW_monitorMode mode; + RGFW_ASSERT(win != NULL); + + mode.w = win->w; + mode.h = win->h; + return RGFW_monitor_requestMode(mon, mode, RGFW_monitorScale); +} + +void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode) { + if (bpp == 32) bpp = 24; + mode->red = mode->green = mode->blue = (u8)(bpp / 3); + + u32 delta = bpp - (mode->red * 3); /* handle leftovers */ + if (delta >= 1) mode->green = mode->green + 1; + if (delta == 2) mode->red = mode->red + 1; +} + +RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request) { + return (((mon.w == mon2.w && mon.h == mon2.h) || !(request & RGFW_monitorScale)) && + ((mon.refreshRate == mon2.refreshRate) || !(request & RGFW_monitorRefresh)) && + ((mon.red == mon2.red && mon.green == mon2.green && mon.blue == mon2.blue) || !(request & RGFW_monitorRGB))); +} + +RGFW_bool RGFW_window_shouldClose(RGFW_window* win) { + return (win == NULL || win->internal.shouldClose || (win->internal.exitKey && RGFW_window_isKeyPressed(win, win->internal.exitKey))); +} + +void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose) { + if (shouldClose) { + RGFW_windowQuitCallback(win); + } else { + win->internal.shouldClose = RGFW_FALSE; + } +} + +#ifndef RGFW_NO_MONITOR +void RGFW_window_scaleToMonitor(RGFW_window* win) { + RGFW_monitor monitor = RGFW_window_getMonitor(win); + if (monitor.scaleX == 0 && monitor.scaleY == 0) + return; + + RGFW_window_resize(win, (i32)(monitor.scaleX * (float)win->w), (i32)(monitor.scaleY * (float)win->h)); +} + +void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) { + RGFW_window_move(win, m.x + win->x, m.y + win->y); +} +#endif + +RGFW_surface* RGFW_createSurface(u8* data, i32 w, i32 h, RGFW_format format) { + RGFW_surface* surface = (RGFW_surface*)RGFW_ALLOC(sizeof(RGFW_surface)); + RGFW_MEMSET(surface, 0, sizeof(RGFW_surface)); + RGFW_createSurfacePtr(data, w, h, format, surface); + return surface; +} + +void RGFW_surface_free(RGFW_surface* surface) { + RGFW_surface_freePtr(surface); + RGFW_FREE(surface); +} + +RGFW_nativeImage* RGFW_surface_getNativeImage(RGFW_surface* surface) { + return &surface->native; +} + +RGFW_surface* RGFW_window_createSurface(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format) { + RGFW_surface* surface = (RGFW_surface*)RGFW_ALLOC(sizeof(RGFW_surface)); + RGFW_MEMSET(surface, 0, sizeof(RGFW_surface)); + RGFW_window_createSurfacePtr(win, data, w, h, format, surface); + return surface; +} +#ifndef RGFW_X11 +RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + RGFW_UNUSED(win); + return RGFW_createSurfacePtr(data, w, h, format, surface); +} +#endif + +const RGFW_colorLayout RGFW_layouts[RGFW_formatCount] = { + { 0, 1, 2, 3 }, /* RGFW_formatRGB8 */ + { 2, 1, 0, 3 }, /* RGFW_formatBGR8 */ + { 0, 1, 2, 3 }, /* RGFW_formatRGBA8 */ + { 1, 2, 3, 0 }, /* RGFW_formatARGB8 */ + { 2, 1, 0, 3 }, /* RGFW_formatBGRA8 */ + { 3, 2, 1, 0 }, /* RGFW_formatABGR8 */ +}; + + +void RGFW_copyImageData(u8* dest_data, i32 w, i32 h, RGFW_format dest_format, u8* src_data, RGFW_format src_format) { + RGFW_copyImageData64(dest_data, w, h, dest_format, src_data, src_format, RGFW_FALSE); +} + +void RGFW_copyImageData64(u8* dest_data, i32 dest_w, i32 dest_h, RGFW_format dest_format, u8* src_data, RGFW_format src_format, RGFW_bool is64bit) { + RGFW_ASSERT(dest_data && src_data); + + u32 src_channels = (src_format >= RGFW_formatRGBA8) ? 4 : 3; + u32 dest_channels = (dest_format >= RGFW_formatRGBA8) ? 4 : 3; + + u32 pixel_count = (u32)(dest_w * dest_h); + + if (src_format == dest_format) { + RGFW_MEMCPY(dest_data, src_data, pixel_count * dest_channels); + return; + } + + const RGFW_colorLayout* src_layout = &RGFW_layouts[src_format]; + const RGFW_colorLayout* dest_layout = &RGFW_layouts[dest_format]; + + u32 i, i2 = 0; + for (i = 0; i < pixel_count; i++) { + const u8* src_px = &src_data[i * src_channels]; + u8* dst_px = &dest_data[i2 * dest_channels]; + u8 rgba[4] = {0}; + rgba[0] = src_px[src_layout->r]; + rgba[1] = src_px[src_layout->g]; + rgba[2] = src_px[src_layout->b]; + rgba[3] = 255; + if (src_channels == 4) + rgba[3] = src_px[src_layout->a]; + + dst_px[dest_layout->r] = rgba[0]; + dst_px[dest_layout->g] = rgba[1]; + dst_px[dest_layout->b] = rgba[2]; + if (dest_channels == 4) + dst_px[dest_layout->a] = rgba[3]; + + i2 += 1 + is64bit; + } +} + +RGFW_monitorNode* RGFW_monitors_add(RGFW_monitor mon) { + RGFW_monitorNode* node = NULL; + if (_RGFW->monitors.freeList.head == NULL) return node; + + node = _RGFW->monitors.freeList.head; + mon = node->mon; + + _RGFW->monitors.freeList.head = node->next; + if (_RGFW->monitors.freeList.head == NULL) { + _RGFW->monitors.freeList.cur = NULL; + } + + node->next = NULL; + + if (_RGFW->monitors.list.head == NULL) { + _RGFW->monitors.list.head = node; + } else { + _RGFW->monitors.list.cur->next = node; + } + + _RGFW->monitors.list.cur = node; + + node->mon = mon; + _RGFW->monitors.count += 1; + return node; +} + +void RGFW_monitors_remove(RGFW_monitorNode* node, RGFW_monitorNode* prev) { + _RGFW->monitors.count -= 1; + + /* remove node from the list */ + if (prev != node) { + prev->next = node->next; + } else { /* node is the head */ + _RGFW->monitors.list.head = NULL; + } + + node->next = NULL; + + /* move node to the free list */ + if (_RGFW->monitors.freeList.head == NULL) { + _RGFW->monitors.freeList.head = node; + } else { + _RGFW->monitors.freeList.cur->next = node; + } + + _RGFW->monitors.freeList.cur = node; +} + +RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format) { + return RGFW_window_setIconEx(win, data, w, h, format, RGFW_iconBoth); +} + +void RGFW_window_captureMouse(RGFW_window* win, RGFW_bool state) { + win->internal.captureMouse = state; + RGFW_window_captureMousePlatform(win, state); +} + +void RGFW_window_setRawMouseMode(RGFW_window* win, RGFW_bool state) { + win->internal.rawMouse = state; + RGFW_window_setRawMouseModePlatform(win, state); +} + +void RGFW_window_captureRawMouse(RGFW_window* win, RGFW_bool state) { + RGFW_window_captureMouse(win, state); + RGFW_window_setRawMouseMode(win, state); +} + +void RGFW_window_holdMouse(RGFW_window* win) { RGFW_window_captureRawMouse(win, RGFW_TRUE); } +void RGFW_window_unholdMouse(RGFW_window* win) { RGFW_window_captureRawMouse(win, RGFW_FALSE); } + +RGFW_bool RGFW_window_isRawMouseMode(RGFW_window* win) { return RGFW_BOOL(win->internal.rawMouse); } +RGFW_bool RGFW_window_isCaptured(RGFW_window* win) { return RGFW_BOOL(win->internal.captureMouse); } +RGFW_bool RGFW_window_isHoldingMouse(RGFW_window* win) { return RGFW_window_isRawMouseMode(win) && RGFW_window_isCaptured(win); } + +void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value) { + if (value) win->internal.mod |= mod; + else win->internal.mod &= ~mod; +} + +void RGFW_updateKeyModsEx(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) { + RGFW_updateKeyMod(win, RGFW_modCapsLock, capital); + RGFW_updateKeyMod(win, RGFW_modNumLock, numlock); + RGFW_updateKeyMod(win, RGFW_modControl, control); + RGFW_updateKeyMod(win, RGFW_modAlt, alt); + RGFW_updateKeyMod(win, RGFW_modShift, shift); + RGFW_updateKeyMod(win, RGFW_modSuper, super); + RGFW_updateKeyMod(win, RGFW_modScrollLock, scroll); +} + +void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll) { + RGFW_updateKeyModsEx(win, capital, numlock, + RGFW_window_isKeyDown(win, RGFW_controlL) || RGFW_window_isKeyDown(win, RGFW_controlR), + RGFW_window_isKeyDown(win, RGFW_altL) || RGFW_window_isKeyDown(win, RGFW_altR), + RGFW_window_isKeyDown(win, RGFW_shiftL) || RGFW_window_isKeyDown(win, RGFW_shiftR), + RGFW_window_isKeyDown(win, RGFW_superL) || RGFW_window_isKeyDown(win, RGFW_superR), + scroll); +} + +void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show) { + if (show && (win->internal.flags & RGFW_windowHideMouse)) + win->internal.flags ^= RGFW_windowHideMouse; + else if (!show && !(win->internal.flags & RGFW_windowHideMouse)) + win->internal.flags |= RGFW_windowHideMouse; +} + +RGFW_bool RGFW_window_isMouseHidden(RGFW_window* win) { + return (RGFW_bool)RGFW_BOOL(((RGFW_window*)win)->internal.flags & RGFW_windowHideMouse); +} + +RGFW_bool RGFW_window_borderless(RGFW_window* win) { + return (RGFW_bool)RGFW_BOOL(win->internal.flags & RGFW_windowNoBorder); +} + +RGFW_bool RGFW_window_isFullscreen(RGFW_window* win){ return RGFW_BOOL(win->internal.flags & RGFW_windowFullscreen); } +RGFW_bool RGFW_window_allowsDND(RGFW_window* win) { return RGFW_BOOL(win->internal.flags & RGFW_windowAllowDND); } + +#ifndef RGFW_WINDOWS +void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) { + RGFW_setBit(&win->internal.flags, RGFW_windowAllowDND, allow); +} +#endif + +#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND) +#ifndef __USE_POSIX199309 + #define __USE_POSIX199309 +#endif +#include +struct timespec; +#endif + +#if defined(RGFW_WAYLAND) || defined(RGFW_X11) || defined(RGFW_WINDOWS) +void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { + RGFW_window_showMouseFlags(win, show); + if (show == RGFW_FALSE) + RGFW_window_setMouse(win, _RGFW->hiddenMouse); + else + RGFW_window_setMouseDefault(win); +} +#endif + +#ifndef RGFW_MACOS +void RGFW_moveToMacOSResourceDir(void) { } +#endif + +/* + graphics API specific code (end of generic code) + starts here +*/ + + +/* + OpenGL defines start here (Normal, EGL, OSMesa) +*/ + +#if defined(RGFW_OPENGL) +/* EGL, OpenGL */ +#define RGFW_DEFAULT_GL_HINTS { \ + /* Stencil */ 0, \ + /* Samples */ 0, \ + /* Stereo */ RGFW_FALSE, \ + /* AuxBuffers */ 0, \ + /* DoubleBuffer */ RGFW_TRUE, \ + /* Red */ 8, \ + /* Green */ 8, \ + /* Blue */ 8, \ + /* Alpha */ 8, \ + /* Depth */ 24, \ + /* AccumRed */ 0, \ + /* AccumGreen */ 0, \ + /* AccumBlue */ 0, \ + /* AccumAlpha */ 0, \ + /* SRGB */ RGFW_FALSE, \ + /* Robustness */ RGFW_FALSE, \ + /* Debug */ RGFW_FALSE, \ + /* NoError */ RGFW_FALSE, \ + /* ReleaseBehavior */ RGFW_glReleaseNone, \ + /* Profile */ RGFW_glCore, \ + /* Major */ 1, \ + /* Minor */ 0, \ + /* Share */ NULL, \ + /* Share_EGL */ NULL, \ + /* renderer */ RGFW_glAccelerated \ +} + +RGFW_glHints RGFW_globalHints_OpenGL_SRC = RGFW_DEFAULT_GL_HINTS; +RGFW_glHints* RGFW_globalHints_OpenGL = &RGFW_globalHints_OpenGL_SRC; + +void RGFW_resetGlobalHints_OpenGL(void) { +#if !defined(__cplusplus) || defined(RGFW_MACOS) + RGFW_globalHints_OpenGL_SRC = (RGFW_glHints)RGFW_DEFAULT_GL_HINTS; +#else + RGFW_globalHints_OpenGL_SRC = RGFW_DEFAULT_GL_HINTS; +#endif +} +void RGFW_setGlobalHints_OpenGL(RGFW_glHints* hints) { RGFW_globalHints_OpenGL = hints; } +RGFW_glHints* RGFW_getGlobalHints_OpenGL(void) { RGFW_init(); return RGFW_globalHints_OpenGL; } + + +void* RGFW_glContext_getSourceContext(RGFW_glContext* ctx) { + RGFW_UNUSED(ctx); + +#ifdef RGFW_WAYLAND + if (RGFW_usingWayland()) return (void*)ctx->egl.ctx; +#endif + +#if defined(RGFW_X11) + return (void*)ctx->ctx; +#else + return NULL; +#endif +} + +RGFW_glContext* RGFW_window_createContext_OpenGL(RGFW_window* win, RGFW_glHints* hints) { + #ifdef RGFW_WAYLAND + if (RGFW_usingWayland()) { + return (RGFW_glContext*)RGFW_window_createContext_EGL(win, hints); + } + #endif + RGFW_glContext* ctx = (RGFW_glContext*)RGFW_ALLOC(sizeof(RGFW_glContext)); + if (RGFW_window_createContextPtr_OpenGL(win, ctx, hints) == RGFW_FALSE) { + RGFW_FREE(ctx); + win->src.ctx.native = NULL; + return NULL; + } + win->src.gfxType |= RGFW_gfxOwnedByRGFW; + return ctx; +} + +RGFW_glContext* RGFW_window_getContext_OpenGL(RGFW_window* win) { + if (win->src.gfxType & RGFW_windowEGL) return NULL; + return win->src.ctx.native; +} + +void RGFW_window_deleteContext_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { + RGFW_window_deleteContextPtr_OpenGL(win, ctx); + if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(ctx); +} + +RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len) { + const char *start = extensions; + const char *where; + const char* terminator; + + if (extensions == NULL || ext == NULL) { + return RGFW_FALSE; + } + + while (ext[len - 1] == '\0' && len > 3) { + len--; + } + + where = RGFW_STRSTR(extensions, ext); + while (where) { + terminator = where + len; + if ((where == start || *(where - 1) == ' ') && + (*terminator == ' ' || *terminator == '\0')) { + return RGFW_TRUE; + } + where = RGFW_STRSTR(terminator, ext); + } + + return RGFW_FALSE; +} + +RGFWDEF RGFW_bool RGFW_extensionSupported_base(const char* extension, size_t len); +RGFW_bool RGFW_extensionSupported_base(const char* extension, size_t len) { + #ifdef GL_NUM_EXTENSIONS + if (RGFW_globalHints_OpenGL->major >= 3) { + i32 i; + + GLint count = 0; + + RGFW_proc RGFW_glGetStringi = RGFW_getProcAddress_OpenGL("glGetStringi"); + RGFW_proc RGFW_glGetIntegerv = RGFW_getProcAddress_OpenGL("glGetIntegerv"); + if (RGFW_glGetIntegerv) + ((void(*)(GLenum, GLint*))RGFW_glGetIntegerv)(GL_NUM_EXTENSIONS, &count); + + for (i = 0; RGFW_glGetStringi && i < count; i++) { + const char* en = ((const char* (*)(u32, u32))RGFW_glGetStringi)(GL_EXTENSIONS, (u32)i); + if (en && RGFW_STRNCMP(en, extension, len) == 0) { + return RGFW_TRUE; + } + } + } else +#endif + { + RGFW_proc RGFW_glGetString = RGFW_getProcAddress_OpenGL("glGetString"); + #define RGFW_GL_EXTENSIONS 0x1F03 + if (RGFW_glGetString) { + const char* extensions = ((const char*(*)(u32))RGFW_glGetString)(RGFW_GL_EXTENSIONS); + + if ((extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len)) { + return RGFW_TRUE; + } + } + } + return RGFW_FALSE; +} + +RGFW_bool RGFW_extensionSupported_OpenGL(const char* extension, size_t len) { + if (RGFW_extensionSupported_base(extension, len)) return RGFW_TRUE; + return RGFW_extensionSupportedPlatform_OpenGL(extension, len); +} + +void RGFW_window_makeCurrentWindow_OpenGL(RGFW_window* win) { + if (win) { + _RGFW->current = win; + } + + RGFW_window_makeCurrentContext_OpenGL(win); +} + +RGFW_window* RGFW_getCurrentWindow_OpenGL(void) { return _RGFW->current; } +void RGFW_attribStack_init(RGFW_attribStack* stack, i32* attribs, size_t max) { stack->attribs = attribs; stack->count = 0; stack->max = max; } +void RGFW_attribStack_pushAttrib(RGFW_attribStack* stack, i32 attrib) { + RGFW_ASSERT(stack->count < stack->max); + stack->attribs[stack->count] = attrib; + stack->count += 1; +} +void RGFW_attribStack_pushAttribs(RGFW_attribStack* stack, i32 attrib1, i32 attrib2) { + RGFW_attribStack_pushAttrib(stack, attrib1); + RGFW_attribStack_pushAttrib(stack, attrib2); +} + +/* EGL */ +#ifdef RGFW_EGL +#include + +PFNEGLINITIALIZEPROC RGFW_eglInitialize; +PFNEGLGETCONFIGSPROC RGFW_eglGetConfigs; +PFNEGLCHOOSECONFIGPROC RGFW_eglChooseConfig; +PFNEGLCREATEWINDOWSURFACEPROC RGFW_eglCreateWindowSurface; +PFNEGLCREATECONTEXTPROC RGFW_eglCreateContext; +PFNEGLMAKECURRENTPROC RGFW_eglMakeCurrent; +PFNEGLGETDISPLAYPROC RGFW_eglGetDisplay; +PFNEGLSWAPBUFFERSPROC RGFW_eglSwapBuffers; +PFNEGLSWAPINTERVALPROC RGFW_eglSwapInterval; +PFNEGLBINDAPIPROC RGFW_eglBindAPI; +PFNEGLDESTROYCONTEXTPROC RGFW_eglDestroyContext; +PFNEGLTERMINATEPROC RGFW_eglTerminate; +PFNEGLDESTROYSURFACEPROC RGFW_eglDestroySurface; +PFNEGLGETCURRENTCONTEXTPROC RGFW_eglGetCurrentContext; +PFNEGLGETPROCADDRESSPROC RGFW_eglGetProcAddress = NULL; +PFNEGLQUERYSTRINGPROC RGFW_eglQueryString; +PFNEGLGETCONFIGATTRIBPROC RGFW_eglGetConfigAttrib; + +#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098 +#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb + +#ifdef RGFW_WINDOWS + #include +#elif defined(RGFW_MACOS) || defined(RGFW_UNIX) + #include +#endif + +#ifdef RGFW_WAYLAND +#include +#endif + +void* RGFW_eglLibHandle = NULL; + +void* RGFW_getDisplay_EGL(void) { return _RGFW->EGL_display; } +void* RGFW_eglContext_getSourceContext(RGFW_eglContext* ctx) { return ctx->ctx; } +void* RGFW_eglContext_getSurface(RGFW_eglContext* ctx) { return ctx->surface; } +struct wl_egl_window* RGFW_eglContext_wlEGLWindow(RGFW_eglContext* ctx) { return ctx->eglWindow; } + +RGFW_bool RGFW_loadEGL(void) { + RGFW_init(); + if (RGFW_eglGetProcAddress != NULL) { + return RGFW_TRUE; + } + +#ifndef RGFW_WASM + #ifdef RGFW_WINDOWS + const char* libNames[] = { "libEGL.dll", "EGL.dll" }; + #elif defined(RGFW_MACOS) || defined(RGFW_UNIX) + /* Linux and macOS */ + const char* libNames[] = { + "libEGL.so.1", /* most common */ + "libEGL.so", /* fallback */ + "/System/Library/Frameworks/OpenGL.framework/OpenGL" /* fallback for older macOS EGL-like systems */ + }; + #endif + + for (size_t i = 0; i < sizeof(libNames) / sizeof(libNames[0]); ++i) { + #ifdef RGFW_WINDOWS + RGFW_eglLibHandle = (void*)LoadLibraryA(libNames[i]); + if (RGFW_eglLibHandle) { + RGFW_eglGetProcAddress = (PFNEGLGETPROCADDRESSPROC)(RGFW_proc)GetProcAddress((HMODULE)RGFW_eglLibHandle, "eglGetProcAddress"); + break; + } + #elif defined(RGFW_MACOS) || defined(RGFW_UNIX) + RGFW_eglLibHandle = dlopen(libNames[i], RTLD_LAZY | RTLD_GLOBAL); + if (RGFW_eglLibHandle) { + void* lib = dlsym(RGFW_eglLibHandle, "eglGetProcAddress"); + if (lib != NULL) RGFW_MEMCPY(&RGFW_eglGetProcAddress, &lib, sizeof(PFNEGLGETPROCADDRESSPROC)); + break; + } + #endif + } + + if (!RGFW_eglLibHandle || !RGFW_eglGetProcAddress) { + return RGFW_FALSE; + } + + RGFW_eglInitialize = (PFNEGLINITIALIZEPROC) RGFW_eglGetProcAddress("eglInitialize"); + RGFW_eglGetConfigs = (PFNEGLGETCONFIGSPROC) RGFW_eglGetProcAddress("eglGetConfigs"); + RGFW_eglChooseConfig = (PFNEGLCHOOSECONFIGPROC) RGFW_eglGetProcAddress("eglChooseConfig"); + RGFW_eglCreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) RGFW_eglGetProcAddress("eglCreateWindowSurface"); + RGFW_eglCreateContext = (PFNEGLCREATECONTEXTPROC) RGFW_eglGetProcAddress("eglCreateContext"); + RGFW_eglMakeCurrent = (PFNEGLMAKECURRENTPROC) RGFW_eglGetProcAddress("eglMakeCurrent"); + RGFW_eglGetDisplay = (PFNEGLGETDISPLAYPROC) RGFW_eglGetProcAddress("eglGetDisplay"); + RGFW_eglSwapBuffers = (PFNEGLSWAPBUFFERSPROC) RGFW_eglGetProcAddress("eglSwapBuffers"); + RGFW_eglSwapInterval = (PFNEGLSWAPINTERVALPROC) RGFW_eglGetProcAddress("eglSwapInterval"); + RGFW_eglBindAPI = (PFNEGLBINDAPIPROC) RGFW_eglGetProcAddress("eglBindAPI"); + RGFW_eglDestroyContext = (PFNEGLDESTROYCONTEXTPROC) RGFW_eglGetProcAddress("eglDestroyContext"); + RGFW_eglTerminate = (PFNEGLTERMINATEPROC) RGFW_eglGetProcAddress("eglTerminate"); + RGFW_eglDestroySurface = (PFNEGLDESTROYSURFACEPROC) RGFW_eglGetProcAddress("eglDestroySurface"); + RGFW_eglQueryString = (PFNEGLQUERYSTRINGPROC) RGFW_eglGetProcAddress("eglQueryString"); + RGFW_eglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC) RGFW_eglGetProcAddress("eglGetCurrentContext"); + RGFW_eglGetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) RGFW_eglGetProcAddress("eglGetConfigAttrib"); + +#else + RGFW_eglGetProcAddress = eglGetProcAddress; + RGFW_eglInitialize = (PFNEGLINITIALIZEPROC) eglInitialize; + RGFW_eglGetConfigs = (PFNEGLGETCONFIGSPROC) eglGetConfigs; + RGFW_eglChooseConfig = (PFNEGLCHOOSECONFIGPROC) eglChooseConfig; + RGFW_eglCreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) eglCreateWindowSurface; + RGFW_eglCreateContext = (PFNEGLCREATECONTEXTPROC) eglCreateContext; + RGFW_eglMakeCurrent = (PFNEGLMAKECURRENTPROC) eglMakeCurrent; + RGFW_eglGetDisplay = (PFNEGLGETDISPLAYPROC) eglGetDisplay; + RGFW_eglSwapBuffers = (PFNEGLSWAPBUFFERSPROC) eglSwapBuffers; + RGFW_eglSwapInterval = (PFNEGLSWAPINTERVALPROC) eglSwapInterval; + RGFW_eglBindAPI = (PFNEGLBINDAPIPROC) eglBindAPI; + RGFW_eglDestroyContext = (PFNEGLDESTROYCONTEXTPROC) eglDestroyContext; + RGFW_eglTerminate = (PFNEGLTERMINATEPROC) eglTerminate; + RGFW_eglDestroySurface = (PFNEGLDESTROYSURFACEPROC) eglDestroySurface; + RGFW_eglQueryString = (PFNEGLQUERYSTRINGPROC) eglQueryString; + RGFW_eglGetCurrentContext = (PFNEGLGETCURRENTCONTEXTPROC) eglGetCurrentContext; + RGFW_eglGetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC)eglGetConfigAttrib; +#endif + + RGFW_bool out = RGFW_BOOL(RGFW_eglInitialize!= NULL && + RGFW_eglGetConfigs!= NULL && + RGFW_eglChooseConfig!= NULL && + RGFW_eglCreateWindowSurface!= NULL && + RGFW_eglCreateContext!= NULL && + RGFW_eglMakeCurrent!= NULL && + RGFW_eglGetDisplay!= NULL && + RGFW_eglSwapBuffers!= NULL && + RGFW_eglSwapInterval != NULL && + RGFW_eglBindAPI!= NULL && + RGFW_eglDestroyContext!= NULL && + RGFW_eglTerminate!= NULL && + RGFW_eglDestroySurface!= NULL && + RGFW_eglQueryString != NULL && + RGFW_eglGetCurrentContext != NULL && + RGFW_eglGetConfigAttrib != NULL); + + if (out) { + #ifdef RGFW_WINDOWS + HDC dc = GetDC(NULL); + _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) dc); + ReleaseDC(NULL, dc); + #elif defined(RGFW_WAYLAND) + if (_RGFW->useWaylandBool) + _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) _RGFW->wl_display); + else + #endif + #ifdef RGFW_X11 + _RGFW->EGL_display = RGFW_eglGetDisplay((EGLNativeDisplayType) _RGFW->display); + #else + {} + #endif + #if !defined(RGFW_WAYLAND) && !defined(RGFW_WINDOWS) && !defined(RGFW_X11) + _RGFW->EGL_display = RGFW_eglGetDisplay(EGL_DEFAULT_DISPLAY); + #endif + } + + RGFW_eglInitialize(_RGFW->EGL_display, NULL, NULL); + return out; +} + + +void RGFW_unloadEGL(void) { + if (!RGFW_eglLibHandle) return; + RGFW_eglTerminate(_RGFW->EGL_display); + #ifdef RGFW_WINDOWS + FreeLibrary((HMODULE)RGFW_eglLibHandle); + #elif defined(RGFW_MACOS) || defined(RGFW_UNIX) + dlclose(RGFW_eglLibHandle); + #endif + + RGFW_eglLibHandle = NULL; + RGFW_eglGetProcAddress = NULL; +} + +RGFW_bool RGFW_window_createContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx, RGFW_glHints* hints) { + if (RGFW_loadEGL() == RGFW_FALSE) return RGFW_FALSE; + win->src.ctx.egl = ctx; + win->src.gfxType = RGFW_gfxEGL; + +#ifdef RGFW_WAYLAND + if (_RGFW->useWaylandBool) + win->src.ctx.egl->eglWindow = wl_egl_window_create(win->src.surface, win->w, win->h); +#endif + + #ifndef EGL_OPENGL_ES1_BIT + #define EGL_OPENGL_ES1_BIT 0x1 + #endif + + EGLint egl_config[24]; + + { + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, egl_config, 24); + + RGFW_attribStack_pushAttribs(&stack, EGL_SURFACE_TYPE, EGL_WINDOW_BIT); + RGFW_attribStack_pushAttrib(&stack, EGL_RENDERABLE_TYPE); + + if (hints->profile == RGFW_glES) { + switch (hints->major) { + case 1: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES1_BIT); break; + case 2: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES2_BIT); break; + case 3: RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_ES3_BIT); break; + default: break; + } + } else { + RGFW_attribStack_pushAttrib(&stack, EGL_OPENGL_BIT); + } + + RGFW_attribStack_pushAttribs(&stack, EGL_RED_SIZE, hints->red); + RGFW_attribStack_pushAttribs(&stack, EGL_GREEN_SIZE, hints->green); + RGFW_attribStack_pushAttribs(&stack, EGL_BLUE_SIZE, hints->blue); + RGFW_attribStack_pushAttribs(&stack, EGL_ALPHA_SIZE, hints->alpha); + RGFW_attribStack_pushAttribs(&stack, EGL_DEPTH_SIZE, hints->depth); + + RGFW_attribStack_pushAttribs(&stack, EGL_STENCIL_SIZE, hints->stencil); + if (hints->samples) { + RGFW_attribStack_pushAttribs(&stack, EGL_SAMPLE_BUFFERS, 1); + RGFW_attribStack_pushAttribs(&stack, EGL_SAMPLES, hints->samples); + } + + RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE); + } + + EGLint numConfigs, best_config = -1, best_samples = 0; + + RGFW_eglChooseConfig(_RGFW->EGL_display, egl_config, NULL, 0, &numConfigs); + EGLConfig* configs = (EGLConfig*)RGFW_ALLOC(sizeof(EGLConfig) * (u32)numConfigs); + + RGFW_eglChooseConfig(_RGFW->EGL_display, egl_config, configs, numConfigs, &numConfigs); + +#ifdef RGFW_X11 + RGFW_bool transparent = (win->internal.flags & RGFW_windowTransparent); + EGLint best_depth = 0; +#endif + + for (EGLint i = 0; i < numConfigs; i++) { + EGLint visual_id = 0; + EGLint samples = 0; + + RGFW_eglGetConfigAttrib(_RGFW->EGL_display, configs[i], EGL_NATIVE_VISUAL_ID, &visual_id); + RGFW_eglGetConfigAttrib(_RGFW->EGL_display, configs[i], EGL_SAMPLES, &samples); + + if (best_config == -1) best_config = i; + +#ifdef RGFW_X11 + if (_RGFW->useWaylandBool == RGFW_FALSE) { + XVisualInfo vinfo_template; + vinfo_template.visualid = (VisualID)visual_id; + + int num_visuals = 0; + XVisualInfo* vi = XGetVisualInfo(_RGFW->display, VisualIDMask, &vinfo_template, &num_visuals); + if (!vi) continue; + if ((!transparent || vi->depth == 32) && best_depth == 0) { + best_config = i; + best_depth = vi->depth; + } + + if ((!(transparent) || vi->depth == 32) && (samples <= hints->samples && samples > best_samples)) { + best_depth = vi->depth; + best_config = i; + best_samples = samples; + XFree(vi); + continue; + } + } +#endif + + if (samples <= hints->samples && samples > best_samples) { + best_config = i; + best_samples = samples; + } + } + + EGLConfig config = configs[best_config]; + RGFW_FREE(configs); +#ifdef RGFW_X11 + if (_RGFW->useWaylandBool == RGFW_FALSE) { + /* This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used */ + XVisualInfo* result; + XVisualInfo desired; + EGLint visualID = 0, count = 0; + + RGFW_eglGetConfigAttrib(_RGFW->EGL_display, config, EGL_NATIVE_VISUAL_ID, &visualID); + if (visualID) { + desired.visualid = (VisualID)visualID; + result = XGetVisualInfo(_RGFW->display, VisualIDMask, &desired, &count); + } else RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to fetch a valid EGL VisualID"); + + if (result == NULL || count == 0) { + if (win->src.window == 0) { + /* try to create a EGL context anyway (this will work if you're not using a NVidia driver) */ + win->internal.flags &= ~(u32)RGFW_windowEGL; + RGFW_createWindowPlatform("", win->internal.flags, win); + } + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to find a valid visual for the EGL config"); + } else { + RGFW_bool showWindow = RGFW_FALSE; + if (win->src.window) { + showWindow = (RGFW_window_isMinimized(win) == RGFW_FALSE); + RGFW_window_closePlatform(win); + } + + RGFW_XCreateWindow(*result, "", win->internal.flags, win); + + if (showWindow) { + RGFW_window_show(win); + } + XFree(result); + } + } +#endif + + EGLint surf_attribs[9]; + + { + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, surf_attribs, 9); + + const char present_opaque_str[] = "EGL_EXT_present_opaque"; + RGFW_bool opaque_extension_Found = RGFW_extensionSupportedPlatform_EGL(present_opaque_str, sizeof(present_opaque_str)); + + #ifndef EGL_PRESENT_OPAQUE_EXT + #define EGL_PRESENT_OPAQUE_EXT 0x31df + #endif + + #ifndef EGL_GL_COLORSPACE_KHR + #define EGL_GL_COLORSPACE_KHR 0x309D + #ifndef EGL_GL_COLORSPACE_SRGB_KHR + #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 + #endif + #endif + + const char gl_colorspace_str[] = "EGL_KHR_gl_colorspace"; + RGFW_bool gl_colorspace_Found = RGFW_extensionSupportedPlatform_EGL(gl_colorspace_str, sizeof(gl_colorspace_str)); + + if (hints->sRGB && gl_colorspace_Found) { + RGFW_attribStack_pushAttribs(&stack, EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); + } + + if (!(win->internal.flags & RGFW_windowTransparent) && opaque_extension_Found) + RGFW_attribStack_pushAttribs(&stack, EGL_PRESENT_OPAQUE_EXT, EGL_TRUE); + + if (hints->doubleBuffer == 0) { + RGFW_attribStack_pushAttribs(&stack, EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); + } + + RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE); + } + #if defined(RGFW_MACOS) + void* layer = RGFW_getLayer_OSX(); + + RGFW_window_setLayer_OSX(win, layer); + + win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) layer, surf_attribs); + #elif defined(RGFW_WINDOWS) + win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.window, surf_attribs); + #elif defined(RGFW_WAYLAND) + if (_RGFW->useWaylandBool) + win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.ctx.egl->eglWindow, surf_attribs); + else + #endif + #ifdef RGFW_X11 + win->src.ctx.egl->surface = RGFW_eglCreateWindowSurface(_RGFW->EGL_display, config, (EGLNativeWindowType) win->src.window, surf_attribs); + #else + {} + #endif + #ifdef RGFW_WASM + win->src.ctx.egl->surface = eglCreateWindowSurface(_RGFW->EGL_display, config, 0, 0); + #endif + + if (win->src.ctx.egl->surface == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to create an EGL surface."); + return RGFW_FALSE; + } + + EGLint attribs[20]; + { + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, attribs, 20); + + if (hints->major || hints->minor) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_MAJOR_VERSION, hints->major); + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_MINOR_VERSION, hints->minor); + } + + if (hints->profile == RGFW_glCore) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT); + } else if (hints->profile == RGFW_glCompatibility) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT); + } else if (hints->profile == RGFW_glForwardCompatibility) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE, EGL_TRUE); + } + + + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_ROBUST_ACCESS, hints->robustness); + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_OPENGL_DEBUG, hints->debug); + + #ifndef EGL_CONTEXT_RELEASE_BEHAVIOR_KHR + #define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 + #endif + + #ifndef EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR + #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 + #endif + + if (hints->releaseBehavior == RGFW_glReleaseFlush) { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); + } else { + RGFW_attribStack_pushAttribs(&stack, EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, 0x0000); + } + + RGFW_attribStack_pushAttribs(&stack, EGL_NONE, EGL_NONE); + } + + if (hints->profile == RGFW_glES) + RGFW_eglBindAPI(EGL_OPENGL_ES_API); + else + RGFW_eglBindAPI(EGL_OPENGL_API); + + win->src.ctx.egl->ctx = RGFW_eglCreateContext(_RGFW->EGL_display, config, hints->shareEGL, attribs); + + if (win->src.ctx.egl->ctx == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, "Failed to create an EGL context."); + return RGFW_FALSE; + } + + RGFW_eglMakeCurrent(_RGFW->EGL_display, win->src.ctx.egl->surface, win->src.ctx.egl->surface, win->src.ctx.egl->ctx); + RGFW_eglSwapBuffers(_RGFW->EGL_display, win->src.ctx.egl->surface); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "EGL context initalized."); + return RGFW_TRUE; +} + +RGFW_eglContext* RGFW_window_getContext_EGL(RGFW_window* win) { + if (win->src.gfxType == RGFW_windowOpenGL) return NULL; + return win->src.ctx.egl; +} + +void RGFW_window_deleteContextPtr_EGL(RGFW_window* win, RGFW_eglContext* ctx) { + if (_RGFW->EGL_display == NULL) return; + + RGFW_eglDestroySurface(_RGFW->EGL_display, ctx->surface); + RGFW_eglDestroyContext(_RGFW->EGL_display, ctx->ctx); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "EGL context freed"); + #ifdef RGFW_WAYLAND + if (_RGFW->useWaylandBool == RGFW_FALSE) return; + wl_egl_window_destroy(win->src.ctx.egl->eglWindow); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "EGL window context freed"); + #endif + win->src.ctx.egl = NULL; +} + +void RGFW_window_makeCurrentContext_EGL(RGFW_window* win) { if (win) RGFW_ASSERT(win->src.ctx.egl); + if (win == NULL) + RGFW_eglMakeCurrent(_RGFW->EGL_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + else { + RGFW_eglMakeCurrent(_RGFW->EGL_display, win->src.ctx.egl->surface, win->src.ctx.egl->surface, win->src.ctx.egl->ctx); + } +} + +void RGFW_window_swapBuffers_EGL(RGFW_window* win) { + if (RGFW_eglSwapBuffers) + RGFW_eglSwapBuffers(_RGFW->EGL_display, win->src.ctx.egl->surface); + else RGFW_window_swapBuffers_OpenGL(win); +} + +void* RGFW_getCurrentContext_EGL(void) { + return RGFW_eglGetCurrentContext(); +} + +RGFW_proc RGFW_getProcAddress_EGL(const char* procname) { + #if defined(RGFW_WINDOWS) + RGFW_proc proc = (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname); + + if (proc) + return proc; + #endif + + return (RGFW_proc) RGFW_eglGetProcAddress(procname); +} + +RGFW_bool RGFW_extensionSupportedPlatform_EGL(const char* extension, size_t len) { + if (RGFW_loadEGL() == RGFW_FALSE) return RGFW_FALSE; + const char* extensions = RGFW_eglQueryString(_RGFW->EGL_display, EGL_EXTENSIONS); + return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len); +} + +void RGFW_window_swapInterval_EGL(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); + RGFW_eglSwapInterval(_RGFW->EGL_display, swapInterval); +} + +RGFW_bool RGFW_extensionSupported_EGL(const char* extension, size_t len) { + if (RGFW_extensionSupported_base(extension, len)) return RGFW_TRUE; + return RGFW_extensionSupportedPlatform_EGL(extension, len); +} + +void RGFW_window_makeCurrentWindow_EGL(RGFW_window* win) { + _RGFW->current = win; + RGFW_window_makeCurrentContext_EGL(win); +} + +RGFW_window* RGFW_getCurrentWindow_EGL(void) { return _RGFW->current; } + +RGFW_eglContext* RGFW_window_createContext_EGL(RGFW_window* win, RGFW_glHints* hints) { + RGFW_eglContext* ctx = (RGFW_eglContext*)RGFW_ALLOC(sizeof(RGFW_eglContext)); + if (RGFW_window_createContextPtr_EGL(win, ctx, hints) == RGFW_FALSE) { + RGFW_FREE(ctx); + win->src.ctx.egl = NULL; + return NULL; + } + win->src.gfxType |= RGFW_gfxOwnedByRGFW; + return ctx; +} + +void RGFW_window_deleteContext_EGL(RGFW_window* win, RGFW_eglContext* ctx) { + RGFW_window_deleteContextPtr_EGL(win, ctx); + if (win->src.gfxType & RGFW_gfxOwnedByRGFW) RGFW_FREE(ctx); +} + +#endif /* RGFW_EGL */ + +/* + end of RGFW_EGL defines +*/ +#endif /* end of RGFW_GL (OpenGL, EGL, OSMesa )*/ + +/* + RGFW_VULKAN defines +*/ +#ifdef RGFW_VULKAN +#ifdef RGFW_MACOS +#include +#endif + +const char** RGFW_getRequiredInstanceExtensions_Vulkan(size_t* count) { + static const char* arr[2] = {VK_KHR_SURFACE_EXTENSION_NAME}; + arr[1] = RGFW_VK_SURFACE; + if (count != NULL) *count = 2; + + return (const char**)arr; +} + +#ifndef RGFW_MACOS +VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) { + RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance); + RGFW_ASSERT(surface != NULL); + + *surface = VK_NULL_HANDLE; + +#ifdef RGFW_X11 + + VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) _RGFW->display, (Window) win->src.window }; + return vkCreateXlibSurfaceKHR(instance, &x11, NULL, surface); +#endif +#if defined(RGFW_WAYLAND) + + VkWaylandSurfaceCreateInfoKHR wayland = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, 0, 0, (struct wl_display*) _RGFW->wl_display, (struct wl_surface*) win->src.surface }; + return vkCreateWaylandSurfaceKHR(instance, &wayland, NULL, surface); +#elif defined(RGFW_WINDOWS) + VkWin32SurfaceCreateInfoKHR win32 = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 0, 0, GetModuleHandle(NULL), (HWND)win->src.window }; + + return vkCreateWin32SurfaceKHR(instance, &win32, NULL, surface); +#endif +} +#endif + +RGFW_bool RGFW_getPresentationSupport_Vulkan(VkInstance instance, VkPhysicalDevice physicalDevice, u32 queueFamilyIndex) { + RGFW_ASSERT(instance); + if (_RGFW == NULL) RGFW_init(); +#ifdef RGFW_X11 + + Visual* visual = DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)); + RGFW_bool out = vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW->display, XVisualIDFromVisual(visual)); + return out; +#endif +#if defined(RGFW_WAYLAND) + + RGFW_bool wlout = vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW->wl_display); + return wlout; +#elif defined(RGFW_WINDOWS) +#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11) + return RGFW_FALSE; /* TODO */ +#endif +} +#endif /* end of RGFW_vulkan */ + +/* +This is where OS specific stuff starts +*/ + +/* start of unix (wayland or X11 (unix) ) defines */ + +#ifdef RGFW_UNIX +#include +#include +#include + +void RGFW_stopCheckEvents(void) { + + _RGFW->eventWait_forceStop[2] = 1; + while (1) { + const char byte = 0; + const ssize_t result = write(_RGFW->eventWait_forceStop[1], &byte, 1); + if (result == 1 || result == -1) + break; + } +} + +RGFWDEF u64 RGFW_linux_getTimeNS(void); +u64 RGFW_linux_getTimeNS(void) { + struct timespec ts; + const u64 scale_factor = 1000000000; + clock_gettime(_RGFW->clock, &ts); + return (u64)ts.tv_sec * scale_factor + (u64)ts.tv_nsec; +} + +void RGFW_waitForEvent(i32 waitMS) { + if (waitMS == 0) return; + + if (_RGFW->eventWait_forceStop[0] == 0 || _RGFW->eventWait_forceStop[1] == 0) { + if (pipe(_RGFW->eventWait_forceStop) != -1) { + fcntl(_RGFW->eventWait_forceStop[0], F_GETFL, 0); + fcntl(_RGFW->eventWait_forceStop[0], F_GETFD, 0); + fcntl(_RGFW->eventWait_forceStop[1], F_GETFL, 0); + fcntl(_RGFW->eventWait_forceStop[1], F_GETFD, 0); + } + } + + struct pollfd fds[2]; + fds[0].fd = 0; + fds[0].events = POLLIN; + fds[0].revents = 0; + fds[1].fd = _RGFW->eventWait_forceStop[0]; + fds[1].events = POLLIN; + fds[1].revents = 0; + + + if (RGFW_usingWayland()) { + #ifdef RGFW_WAYLAND + fds[0].fd = wl_display_get_fd(_RGFW->wl_display); + + /* empty the queue */ + while (wl_display_prepare_read(_RGFW->wl_display) != 0) { + /* error occured when dispatching the queue */ + if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) { + return; + } + } + + /* send any pending requests to the compositor */ + while (wl_display_flush(_RGFW->wl_display) == -1) { + + /* queue is full dispatch them */ + if (errno == EAGAIN) { + if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) { + return; + } + } else { + return; + } + } + #endif + } else { + #ifdef RGFW_X11 + fds[0].fd = ConnectionNumber(_RGFW->display); + #endif + } + + + u64 start = RGFW_linux_getTimeNS(); + if (RGFW_usingWayland()) { + #ifdef RGFW_WAYLAND + while (wl_display_dispatch_pending(_RGFW->wl_display) == 0) { + if (poll(fds, 1, waitMS) <= 0) { + wl_display_cancel_read(_RGFW->wl_display); + break; + } else { + if (wl_display_read_events(_RGFW->wl_display) == -1) + return; + } + + if (waitMS != RGFW_eventWaitNext) { + waitMS -= (i32)(RGFW_linux_getTimeNS() - start) / (i32)1e+6; + } + } + + /* queue contains events from read, dispatch them */ + if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) { + return; + } + #endif + } else { + #ifdef RGFW_X11 + while (XPending(_RGFW->display) == 0) { + if (poll(fds, 1, waitMS) <= 0) + break; + + if (waitMS != RGFW_eventWaitNext) { + waitMS -= (i32)(RGFW_linux_getTimeNS() - start) / (i32)1e+6; + } + } + #endif + } + + /* drain any data in the stop request */ + if (_RGFW->eventWait_forceStop[2]) { + char data[64]; + RGFW_MEMSET(data, 0, sizeof(data)); + (void)!read(_RGFW->eventWait_forceStop[0], data, sizeof(data)); + + _RGFW->eventWait_forceStop[2] = 0; + } +} + +char* RGFW_strtok(char* str, const char* delimStr); +char* RGFW_strtok(char* str, const char* delimStr) { + static char* static_str = NULL; + + if (str != NULL) + static_str = str; + + if (static_str == NULL) { + return NULL; + } + + while (*static_str != '\0') { + RGFW_bool delim = 0; + const char* d; + for (d = delimStr; *d != '\0'; d++) { + if (*static_str == *d) { + delim = 1; + break; + } + } + if (!delim) + break; + static_str++; + } + + if (*static_str == '\0') + return NULL; + + char* token_start = static_str; + while (*static_str != '\0') { + int delim = 0; + const char* d; + for (d = delimStr; *d != '\0'; d++) { + if (*static_str == *d) { + delim = 1; + break; + } + } + + if (delim) { + *static_str = '\0'; + static_str++; + break; + } + static_str++; + } + + return token_start; +} + +#ifdef RGFW_X11 +RGFWDEF i32 RGFW_initPlatform_X11(void); +RGFWDEF void RGFW_deinitPlatform_X11(void); +#endif +#ifdef RGFW_WAYLAND +RGFWDEF i32 RGFW_initPlatform_Wayland(void); +RGFWDEF void RGFW_deinitPlatform_Wayland(void); +#endif + +RGFWDEF void RGFW_load_X11(void); +RGFWDEF void RGFW_load_Wayland(void); + +#if !defined(RGFW_X11) || !defined(RGFW_WAYLAND) +void RGFW_load_X11(void) { } +void RGFW_load_Wayland(void) { } +#endif + +/* + * Sadly we have to use magic linux keycodes + * We can't use X11 functions, because that breaks Wayland, but they use the same keycodes so there's no use redeffing them + * We can't use linux enums, because the headers don't exist on BSD + */ +void RGFW_initKeycodesPlatform(void) { + _RGFW->keycodes[49] = RGFW_backtick; + _RGFW->keycodes[19] = RGFW_0; + _RGFW->keycodes[10] = RGFW_1; + _RGFW->keycodes[11] = RGFW_2; + _RGFW->keycodes[12] = RGFW_3; + _RGFW->keycodes[13] = RGFW_4; + _RGFW->keycodes[14] = RGFW_5; + _RGFW->keycodes[15] = RGFW_6; + _RGFW->keycodes[16] = RGFW_7; + _RGFW->keycodes[17] = RGFW_8; + _RGFW->keycodes[18] = RGFW_9; + _RGFW->keycodes[65] = RGFW_space; + _RGFW->keycodes[38] = RGFW_a; + _RGFW->keycodes[56] = RGFW_b; + _RGFW->keycodes[54] = RGFW_c; + _RGFW->keycodes[40] = RGFW_d; + _RGFW->keycodes[26] = RGFW_e; + _RGFW->keycodes[41] = RGFW_f; + _RGFW->keycodes[42] = RGFW_g; + _RGFW->keycodes[43] = RGFW_h; + _RGFW->keycodes[31] = RGFW_i; + _RGFW->keycodes[44] = RGFW_j; + _RGFW->keycodes[45] = RGFW_k; + _RGFW->keycodes[46] = RGFW_l; + _RGFW->keycodes[58] = RGFW_m; + _RGFW->keycodes[57] = RGFW_n; + _RGFW->keycodes[32] = RGFW_o; + _RGFW->keycodes[33] = RGFW_p; + _RGFW->keycodes[24] = RGFW_q; + _RGFW->keycodes[27] = RGFW_r; + _RGFW->keycodes[39] = RGFW_s; + _RGFW->keycodes[28] = RGFW_t; + _RGFW->keycodes[30] = RGFW_u; + _RGFW->keycodes[55] = RGFW_v; + _RGFW->keycodes[25] = RGFW_w; + _RGFW->keycodes[53] = RGFW_x; + _RGFW->keycodes[29] = RGFW_y; + _RGFW->keycodes[52] = RGFW_z; + _RGFW->keycodes[60] = RGFW_period; + _RGFW->keycodes[59] = RGFW_comma; + _RGFW->keycodes[61] = RGFW_slash; + _RGFW->keycodes[34] = RGFW_bracket; + _RGFW->keycodes[35] = RGFW_closeBracket; + _RGFW->keycodes[47] = RGFW_semicolon; + _RGFW->keycodes[48] = RGFW_apostrophe; + _RGFW->keycodes[51] = RGFW_backSlash; + _RGFW->keycodes[36] = RGFW_return; + _RGFW->keycodes[119] = RGFW_delete; + _RGFW->keycodes[77] = RGFW_numLock; + _RGFW->keycodes[106] = RGFW_kpSlash; + _RGFW->keycodes[63] = RGFW_kpMultiply; + _RGFW->keycodes[86] = RGFW_kpPlus; + _RGFW->keycodes[82] = RGFW_kpMinus; + _RGFW->keycodes[87] = RGFW_kp1; + _RGFW->keycodes[88] = RGFW_kp2; + _RGFW->keycodes[89] = RGFW_kp3; + _RGFW->keycodes[83] = RGFW_kp4; + _RGFW->keycodes[84] = RGFW_kp5; + _RGFW->keycodes[85] = RGFW_kp6; + _RGFW->keycodes[81] = RGFW_kp9; + _RGFW->keycodes[90] = RGFW_kp0; + _RGFW->keycodes[91] = RGFW_kpPeriod; + _RGFW->keycodes[104] = RGFW_kpReturn; + _RGFW->keycodes[20] = RGFW_minus; + _RGFW->keycodes[21] = RGFW_equals; + _RGFW->keycodes[22] = RGFW_backSpace; + _RGFW->keycodes[23] = RGFW_tab; + _RGFW->keycodes[66] = RGFW_capsLock; + _RGFW->keycodes[50] = RGFW_shiftL; + _RGFW->keycodes[37] = RGFW_controlL; + _RGFW->keycodes[64] = RGFW_altL; + _RGFW->keycodes[133] = RGFW_superL; + _RGFW->keycodes[105] = RGFW_controlR; + _RGFW->keycodes[134] = RGFW_superR; + _RGFW->keycodes[62] = RGFW_shiftR; + _RGFW->keycodes[108] = RGFW_altR; + _RGFW->keycodes[67] = RGFW_F1; + _RGFW->keycodes[68] = RGFW_F2; + _RGFW->keycodes[69] = RGFW_F3; + _RGFW->keycodes[70] = RGFW_F4; + _RGFW->keycodes[71] = RGFW_F5; + _RGFW->keycodes[72] = RGFW_F6; + _RGFW->keycodes[73] = RGFW_F7; + _RGFW->keycodes[74] = RGFW_F8; + _RGFW->keycodes[75] = RGFW_F9; + _RGFW->keycodes[76] = RGFW_F10; + _RGFW->keycodes[95] = RGFW_F11; + _RGFW->keycodes[96] = RGFW_F12; + _RGFW->keycodes[111] = RGFW_up; + _RGFW->keycodes[116] = RGFW_down; + _RGFW->keycodes[113] = RGFW_left; + _RGFW->keycodes[114] = RGFW_right; + _RGFW->keycodes[118] = RGFW_insert; + _RGFW->keycodes[115] = RGFW_end; + _RGFW->keycodes[112] = RGFW_pageUp; + _RGFW->keycodes[117] = RGFW_pageDown; + _RGFW->keycodes[9] = RGFW_escape; + _RGFW->keycodes[110] = RGFW_home; + _RGFW->keycodes[78] = RGFW_scrollLock; + _RGFW->keycodes[107] = RGFW_printScreen; + _RGFW->keycodes[128] = RGFW_pause; + _RGFW->keycodes[191] = RGFW_F13; + _RGFW->keycodes[192] = RGFW_F14; + _RGFW->keycodes[193] = RGFW_F15; + _RGFW->keycodes[194] = RGFW_F16; + _RGFW->keycodes[195] = RGFW_F17; + _RGFW->keycodes[196] = RGFW_F18; + _RGFW->keycodes[197] = RGFW_F19; + _RGFW->keycodes[198] = RGFW_F20; + _RGFW->keycodes[199] = RGFW_F21; + _RGFW->keycodes[200] = RGFW_F22; + _RGFW->keycodes[201] = RGFW_F23; + _RGFW->keycodes[202] = RGFW_F24; + _RGFW->keycodes[203] = RGFW_F25; + _RGFW->keycodes[142] = RGFW_kpEqual; + _RGFW->keycodes[161] = RGFW_world1; /* non-US key #1 */ + _RGFW->keycodes[162] = RGFW_world2; /* non-US key #2 */ +} + +i32 RGFW_initPlatform(void) { + #if defined(_POSIX_MONOTONIC_CLOCK) + struct timespec ts; + RGFW_MEMSET(&ts, 0, sizeof(struct timespec)); + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + _RGFW->clock = CLOCK_MONOTONIC; + #else + _RGFW->clock = CLOCK_REALTIME; + #endif + +#ifdef RGFW_WAYLAND + RGFW_load_Wayland(); + i32 ret = RGFW_initPlatform_Wayland(); + if (ret == 0) { + return 0; + } else { + #ifdef RGFW_X11 + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, "Falling back to X11"); + RGFW_useWayland(0); + #else + return ret; + #endif + } +#endif +#ifdef RGFW_X11 + RGFW_load_X11(); + return RGFW_initPlatform_X11(); +#else + return 0; +#endif +} + + +void RGFW_deinitPlatform(void) { + if (_RGFW->eventWait_forceStop[0] || _RGFW->eventWait_forceStop[1]){ + close(_RGFW->eventWait_forceStop[0]); + close(_RGFW->eventWait_forceStop[1]); + } +#ifdef RGFW_WAYLAND + if (_RGFW->useWaylandBool) { + RGFW_deinitPlatform_Wayland(); + return; + } +#endif +#ifdef RGFW_X11 + RGFW_deinitPlatform_X11(); +#endif +} + +static size_t RGFW_unix_stringlen(const char* name) { + size_t i = 0; + while (name[i]) { i++; } + return i; +} + +#endif /* end of wayland or X11 defines */ + + +/* + + +Start of Linux / Unix defines + + +*/ + +#ifdef RGFW_X11 +#ifdef RGFW_WAYLAND +#define RGFW_FUNC(func) func##_X11 +#else +#define RGFW_FUNC(func) func +#endif + +#include +#include + +#include /* for data limits (mainly used in drag and drop functions) */ +#include + +void RGFW_setXInstName(const char* name) { _RGFW->instName = name; } +#if !defined(RGFW_NO_X11_CURSOR) && defined(RGFW_X11) + #include +#endif + +#ifndef RGFW_NO_DPI + #include + #include +#endif + +#include +#include +#include + +#include /* for converting keycode to string */ +#include /* for hiding */ +#include +#include +#include + +#ifdef RGFW_OPENGL + #ifndef __gl_h_ + #define __gl_h_ + #define RGFW_gl_ndef + #define GLubyte unsigned char + #define GLenum unsigned int + #define GLint int + #define GLuint unsigned int + #define GLsizei int + #define GLfloat float + #define GLvoid void + #define GLbitfield unsigned int + #define GLintptr ptrdiff_t + #define GLsizeiptr ptrdiff_t + #define GLboolean unsigned char + #endif + + #include /* GLX defs, xlib.h, gl.h */ + #ifndef GLX_MESA_swap_control + #define GLX_MESA_swap_control + #endif + + #ifdef RGFW_gl_ndef + #undef __gl_h_ + #undef GLubyte + #undef GLenum + #undef GLint + #undef GLuint + #undef GLsizei + #undef GLfloat + #undef GLvoid + #undef GLbitfield + #undef GLintptr + #undef GLsizeiptr + #undef GLboolean + #endif + typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); +#endif + +/* atoms needed for drag and drop */ +#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int); + typedef void (*PFN_XcursorImageDestroy)(XcursorImage*); + typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*); +#endif + +#if !defined(RGFW_NO_X11_XI_PRELOAD) + typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); + PFN_XISelectEvents XISelectEventsSRC = NULL; + #define XISelectEvents XISelectEventsSRC + + void* X11Xihandle = NULL; +#endif + +#if !defined(RGFW_NO_X11_EXT_PRELOAD) + typedef void (* PFN_XSyncIntToValue)(XSyncValue*, int); + PFN_XSyncIntToValue XSyncIntToValueSRC = NULL; + #define XSyncIntToValue XSyncIntToValueSRC + + typedef Status (* PFN_XSyncSetCounter)(Display*, XSyncCounter, XSyncValue); + PFN_XSyncSetCounter XSyncSetCounterSRC = NULL; + #define XSyncSetCounter XSyncSetCounterSRC + + typedef XSyncCounter (* PFN_XSyncCreateCounter)(Display*, XSyncValue); + PFN_XSyncCreateCounter XSyncCreateCounterSRC = NULL; + #define XSyncCreateCounter XSyncCreateCounterSRC + + typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int); + PFN_XShapeCombineMask XShapeCombineMaskSRC; + #define XShapeCombineMask XShapeCombineMaskSRC + + typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int); + PFN_XShapeCombineRegion XShapeCombineRegionSRC; + #define XShapeCombineRegion XShapeCombineRegionSRC + void* X11XEXThandle = NULL; +#endif + +#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + PFN_XcursorImageLoadCursor XcursorImageLoadCursorSRC = NULL; + PFN_XcursorImageCreate XcursorImageCreateSRC = NULL; + PFN_XcursorImageDestroy XcursorImageDestroySRC = NULL; + + #define XcursorImageLoadCursor XcursorImageLoadCursorSRC + #define XcursorImageCreate XcursorImageCreateSRC + #define XcursorImageDestroy XcursorImageDestroySRC + + void* X11Cursorhandle = NULL; +#endif + +RGFWDEF RGFW_bool RGFW_waitForShowEvent_X11(RGFW_window* win); +RGFW_bool RGFW_waitForShowEvent_X11(RGFW_window* win) { + XEvent dummy; + while (!XCheckTypedWindowEvent(_RGFW->display, win->src.window, VisibilityNotify, &dummy)) { + RGFW_waitForEvent(100); + } + + return RGFW_TRUE; +} + +void* RGFW_getDisplay_X11(void) { return _RGFW->display; } +u64 RGFW_window_getWindow_X11(RGFW_window* win) { return (u64)win->src.window; } + +RGFWDEF RGFW_format RGFW_XImage_getFormat(XImage* image); +RGFW_format RGFW_XImage_getFormat(XImage* image) { + switch (image->bits_per_pixel) { + case 24: + if (image->red_mask == 0xFF0000 && image->green_mask == 0x00FF00 && image->blue_mask == 0x0000FF) + return RGFW_formatRGB8; + if (image->red_mask == 0x0000FF && image->green_mask == 0x00FF00 && image->blue_mask == 0xFF0000) + return RGFW_formatBGR8; + break; + case 32: + if (image->red_mask == 0x00FF0000 && image->green_mask == 0x0000FF00 && image->blue_mask == 0x000000FF) + return RGFW_formatBGRA8; + if (image->red_mask == 0x000000FF && image->green_mask == 0x0000FF00 && image->blue_mask == 0x00FF0000) + return RGFW_formatRGBA8; + if (image->red_mask == 0x0000FF00 && image->green_mask == 0x00FF0000 && image->blue_mask == 0xFF000000) + return RGFW_formatABGR8; + if (image->red_mask == 0x00FF0000 && image->green_mask == 0x0000FF00 && image->blue_mask == 0x000000FF) + return RGFW_formatARGB8; /* ambiguous without alpha */ + break; + } + return RGFW_formatARGB8; +} + +RGFW_bool RGFW_window_createSurfacePtr(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + + XWindowAttributes attrs; + if (XGetWindowAttributes(_RGFW->display, win->src.window, &attrs) == 0) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to get window attributes."); + return RGFW_FALSE; + } + + surface->native.bitmap = XCreateImage(_RGFW->display, attrs.visual, (u32)attrs.depth, + ZPixmap, 0, NULL, (u32)surface->w, (u32)surface->h, 32, 0); + + surface->native.buffer = (u8*)RGFW_ALLOC((size_t)(w * h * 4)); + surface->native.format = RGFW_XImage_getFormat(surface->native.bitmap); + + if (surface->native.bitmap == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to create XImage."); + return RGFW_FALSE; + } + + surface->native.format = RGFW_formatBGRA8; + return RGFW_TRUE; +} + +RGFW_bool RGFW_FUNC(RGFW_createSurfacePtr) (u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + return RGFW_window_createSurfacePtr(_RGFW->root, data, w, h, format, surface); +} + +void RGFW_FUNC(RGFW_window_blitSurface) (RGFW_window* win, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + surface->native.bitmap->data = (char*)surface->native.buffer; + RGFW_copyImageData((u8*)surface->native.buffer, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format); + + XPutImage(_RGFW->display, win->src.window, win->src.gc, surface->native.bitmap, 0, 0, 0, 0, (u32)RGFW_MIN(win->w, surface->w), (u32)RGFW_MIN(win->h, surface->h)); + surface->native.bitmap->data = NULL; + return; +} + +void RGFW_FUNC(RGFW_surface_freePtr) (RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + RGFW_FREE(surface->native.buffer); + XDestroyImage(surface->native.bitmap); + return; +} + +#define RGFW_LOAD_ATOM(name) \ + static Atom name = 0; \ + if (name == 0) name = XInternAtom(_RGFW->display, #name, False); + +void RGFW_FUNC(RGFW_window_setBorder) (RGFW_window* win, RGFW_bool border) { + RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border); + RGFW_LOAD_ATOM(_MOTIF_WM_HINTS); + + struct __x11WindowHints { + unsigned long flags, functions, decorations, status; + long input_mode; + } hints; + hints.flags = 2; + hints.decorations = border; + + XChangeProperty(_RGFW->display, win->src.window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32, PropModeReplace, (u8*)&hints, 5); + + if (RGFW_window_isHidden(win) == 0) { + RGFW_window_hide(win); + RGFW_window_show(win); + } +} + +void RGFW_FUNC(RGFW_window_setRawMouseModePlatform) (RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); RGFW_UNUSED(state); +} + +void RGFW_FUNC(RGFW_window_captureMousePlatform) (RGFW_window* win, RGFW_bool state) { + if (state) { + unsigned int event_mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask; + XGrabPointer(_RGFW->display, win->src.window, True, event_mask, GrabModeAsync, GrabModeAsync, win->src.window, None, CurrentTime); + } else { + XUngrabPointer(_RGFW->display, CurrentTime); + } +} + +#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = dlopen(lib, RTLD_LAZY | RTLD_LOCAL) +#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \ + void* ptr = dlsym(proc, #name); \ + if (ptr != NULL) RGFW_MEMCPY(&name##SRC, &ptr, sizeof(PFN_##name)); \ +} + +RGFWDEF void RGFW_window_getVisual(XVisualInfo* visual, RGFW_bool transparent); +void RGFW_window_getVisual(XVisualInfo* visual, RGFW_bool transparent) { + visual->visual = DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)); + visual->depth = DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display)); + if (transparent) { + XMatchVisualInfo(_RGFW->display, DefaultScreen(_RGFW->display), 32, TrueColor, visual); /*!< for RGBA backgrounds */ + if (visual->depth != 32) + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to load a 32-bit depth."); + } +} + +RGFWDEF int RGFW_XErrorHandler(Display* display, XErrorEvent* ev); +int RGFW_XErrorHandler(Display* display, XErrorEvent* ev) { + char errorText[512]; + XGetErrorText(display, ev->error_code, errorText, sizeof(errorText)); + + char buf[1024]; + RGFW_SNPRINTF(buf, sizeof(buf), "[X Error] %s\n Error code: %d\n Request code: %d\n Minor code: %d\n Serial: %lu\n", + errorText, + ev->error_code, ev->request_code, ev->minor_code, ev->serial); + + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errX11, buf); + _RGFW->x11Error = ev; + return 0; +} + +void RGFW_XCreateWindow (XVisualInfo visual, const char* name, RGFW_windowFlags flags, RGFW_window* win) { + i64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | + LeaveWindowMask | EnterWindowMask | ExposureMask | VisibilityChangeMask | PropertyChangeMask; + + /* make X window attrubutes */ + XSetWindowAttributes swa; + RGFW_MEMSET(&swa, 0, sizeof(swa)); + + win->src.parent = DefaultRootWindow(_RGFW->display); + + Colormap cmap; + swa.colormap = cmap = XCreateColormap(_RGFW->display, + win->src.parent, + visual.visual, AllocNone); + swa.event_mask = event_mask; + swa.background_pixmap = None; + + /* create the window */ + win->src.window = XCreateWindow(_RGFW->display, win->src.parent, win->x, win->y, (u32)win->w, (u32)win->h, + 0, visual.depth, InputOutput, visual.visual, + CWBorderPixel | CWColormap | CWEventMask, &swa); + + win->src.flashEnd = 0; + + XFreeColors(_RGFW->display, cmap, NULL, 0, 0); + + XSaveContext(_RGFW->display, win->src.window, _RGFW->context, (XPointer)win); + + win->src.gc = XCreateGC(_RGFW->display, win->src.window, 0, NULL); + + /* In your .desktop app, if you set the property + StartupWMClass=RGFW that will assoicate the launcher icon + with your application - robrohan */ + if (_RGFW->className == NULL) + _RGFW->className = (char*)name; + + XClassHint hint; + hint.res_class = (char*)_RGFW->className; + + if (_RGFW->instName == NULL) hint.res_name = (char*)name; + else hint.res_name = (char*)_RGFW->instName; + + XSetClassHint(_RGFW->display, win->src.window, &hint); + + XWMHints hints; + hints.flags = StateHint; + hints.initial_state = NormalState; + + XSetWMHints(_RGFW->display, win->src.window, &hints); + + #ifndef RGFW_NO_MONITOR + if (flags & RGFW_windowScaleToMonitor) + RGFW_window_scaleToMonitor(win); + #endif + XSelectInput(_RGFW->display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want */ + + /* make it so the user can't close the window until the program does */ + RGFW_LOAD_ATOM(WM_DELETE_WINDOW); + XSetWMProtocols(_RGFW->display, (Drawable) win->src.window, &WM_DELETE_WINDOW, 1); + /* set the background */ + RGFW_window_setName(win, name); + + XMoveWindow(_RGFW->display, (Drawable) win->src.window, win->x, win->y); /*!< move the window to it's proper cords */ + + if (flags & RGFW_windowAllowDND) { /* init drag and drop atoms and turn on drag and drop for this window */ + win->internal.flags |= RGFW_windowAllowDND; + + /* actions */ + Atom XdndAware = XInternAtom(_RGFW->display, "XdndAware", False); + const u8 version = 5; + + XChangeProperty(_RGFW->display, win->src.window, + XdndAware, 4, 32, + PropModeReplace, &version, 1); /*!< turns on drag and drop */ + } + +#ifdef RGFW_ADVANCED_SMOOTH_RESIZE + RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST_COUNTER) + RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST) + + Atom protcols[2] = {_NET_WM_SYNC_REQUEST, WM_DELETE_WINDOW}; + XSetWMProtocols(_RGFW->display, win->src.window, protcols, 2); + + XSyncValue initial_value; + XSyncIntToValue(&initial_value, 0); + win->src.counter = XSyncCreateCounter(_RGFW->display, initial_value); + + XChangeProperty(_RGFW->display, win->src.window, _NET_WM_SYNC_REQUEST_COUNTER, XA_CARDINAL, 32, PropModeReplace, (u8*)&win->src.counter, 1); +#endif + + win->src.x = win->x; + win->src.y = win->y; + win->src.w = win->w; + win->src.h = win->h; + + XSetWindowBackground(_RGFW->display, win->src.window, None); + XClearWindow(_RGFW->display, win->src.window); + + /* stupid hack to make resizing the window less bad */ + XSetWindowBackgroundPixmap(_RGFW->display, win->src.window, None); +} + +RGFW_window* RGFW_FUNC(RGFW_createWindowPlatform) (const char* name, RGFW_windowFlags flags, RGFW_window* win) { + if ((flags & RGFW_windowOpenGL) || (flags & RGFW_windowEGL)) { + win->src.window = 0; + return win; + } + + XVisualInfo visual; + RGFW_window_getVisual(&visual, RGFW_BOOL(win->internal.flags & RGFW_windowTransparent)); + RGFW_XCreateWindow(visual, name, flags, win); + return win; /*return newly created window */ +} + +RGFW_bool RGFW_FUNC(RGFW_getGlobalMouse) (i32* fX, i32* fY) { + RGFW_init(); + i32 x, y; + u32 z; + Window window1, window2; + XQueryPointer(_RGFW->display, XDefaultRootWindow(_RGFW->display), &window1, &window2, fX, fY, &x, &y, &z); + return RGFW_TRUE; +} + +RGFWDEF void RGFW_XHandleClipboardSelection(XEvent* event); +void RGFW_XHandleClipboardSelection(XEvent* event) { RGFW_UNUSED(event); + RGFW_LOAD_ATOM(ATOM_PAIR); + RGFW_LOAD_ATOM(MULTIPLE); + RGFW_LOAD_ATOM(TARGETS); + RGFW_LOAD_ATOM(SAVE_TARGETS); + RGFW_LOAD_ATOM(UTF8_STRING); + + const XSelectionRequestEvent* request = &event->xselectionrequest; + Atom formats[2] = {0}; + formats[0] = UTF8_STRING; + formats[1] = XA_STRING; + const int formatCount = sizeof(formats) / sizeof(formats[0]); + + if (request->target == TARGETS) { + Atom targets[4] = {0}; + targets[0] = TARGETS; + targets[1] = MULTIPLE; + targets[2] = UTF8_STRING; + targets[3] = XA_STRING; + + XChangeProperty(_RGFW->display, request->requestor, request->property, + XA_ATOM, 32, PropModeReplace, (u8*) targets, sizeof(targets) / sizeof(Atom)); + } else if (request->target == MULTIPLE) { + Atom* targets = NULL; + + Atom actualType = 0; + int actualFormat = 0; + unsigned long count = 0, bytesAfter = 0; + + XGetWindowProperty(_RGFW->display, request->requestor, request->property, 0, LONG_MAX, + False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets); + + unsigned long i; + for (i = 0; i < (u32)count; i += 2) { + if (targets[i] == UTF8_STRING || targets[i] == XA_STRING) + XChangeProperty(_RGFW->display, request->requestor, targets[i + 1], targets[i], + 8, PropModeReplace, (const unsigned char *)_RGFW->clipboard, (i32)_RGFW->clipboard_len); + else + targets[i + 1] = None; + } + + XChangeProperty(_RGFW->display, + request->requestor, request->property, ATOM_PAIR, 32, + PropModeReplace, (u8*) targets, (i32)count); + + XFlush(_RGFW->display); + XFree(targets); + } else if (request->target == SAVE_TARGETS) + XChangeProperty(_RGFW->display, request->requestor, request->property, 0, 32, PropModeReplace, NULL, 0); + else { + int i; + for (i = 0; i < formatCount; i++) { + if (request->target != formats[i]) + continue; + XChangeProperty(_RGFW->display, request->requestor, request->property, request->target, + 8, PropModeReplace, (u8*) _RGFW->clipboard, (i32)_RGFW->clipboard_len); + } + } + + XEvent reply = { SelectionNotify }; + reply.xselection.property = request->property; + reply.xselection.display = request->display; + reply.xselection.requestor = request->requestor; + reply.xselection.selection = request->selection; + reply.xselection.target = request->target; + reply.xselection.time = request->time; + + XSendEvent(_RGFW->display, request->requestor, False, 0, &reply); + XFlush(_RGFW->display); +} + +i32 RGFW_XHandleClipboardSelectionHelper(void); + +u8 RGFW_FUNC(RGFW_rgfwToKeyChar) (u32 key) { + u32 keycode = RGFW_rgfwToApiKey(key); + + Window root = DefaultRootWindow(_RGFW->display); + Window ret_root, ret_child; + int root_x, root_y, win_x, win_y; + unsigned int mask; + XQueryPointer(_RGFW->display, root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask); + KeySym sym = (KeySym)XkbKeycodeToKeysym(_RGFW->display, (KeyCode)keycode, 0, (KeyCode)mask & ShiftMask ? 1 : 0); + + if ((mask & LockMask) && sym >= XK_a && sym <= XK_z) + sym = (mask & ShiftMask) ? sym + 32 : sym - 32; + if ((u8)sym != (u32)sym) + sym = 0; + + return (u8)sym; +} + +RGFWDEF void RGFW_XHandleEvent(void); +void RGFW_XHandleEvent(void) { + RGFW_LOAD_ATOM(XdndTypeList); + RGFW_LOAD_ATOM(XdndSelection); + RGFW_LOAD_ATOM(XdndEnter); + RGFW_LOAD_ATOM(XdndPosition); + RGFW_LOAD_ATOM(XdndStatus); + RGFW_LOAD_ATOM(XdndLeave); + RGFW_LOAD_ATOM(XdndDrop); + RGFW_LOAD_ATOM(XdndFinished); + RGFW_LOAD_ATOM(XdndActionCopy); + RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST); + RGFW_LOAD_ATOM(WM_PROTOCOLS); + RGFW_LOAD_ATOM(WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE); + + /* xdnd data */ + static Window source = 0; + static long version = 0; + static i32 format = 0; + + XEvent reply = { ClientMessage }; + XEvent E; + + static double deltaX = 0.0f; + static double deltaY = 0.0f; + + if (E.type != GenericEvent) { + deltaX = 0.0f; + deltaY = 0.0f; + } + + XNextEvent(_RGFW->display, &E); + switch (E.type) { + case SelectionRequest: + RGFW_XHandleClipboardSelection(&E); + return; + case GenericEvent: { + XGetEventData(_RGFW->display, &E.xcookie); + switch (E.xcookie.evtype) { + case XI_RawMotion: { + XIRawEvent* raw = (XIRawEvent *)E.xcookie.data; + if (raw->valuators.mask_len == 0) { + XFreeEventData(_RGFW->display, &E.xcookie); + return; + } + + if (XIMaskIsSet(raw->valuators.mask, 0) != 0) + deltaX += raw->raw_values[0]; + if (XIMaskIsSet(raw->valuators.mask, 1) != 0) + deltaY += raw->raw_values[1]; + + _RGFW->vectorX = (float)deltaX; + _RGFW->vectorY = (float)deltaY; + } + default: break; + } + + XFreeEventData(_RGFW->display, &E.xcookie); + return; + } + } + + RGFW_window* win = NULL; + if (XFindContext(_RGFW->display, E.xany.window, _RGFW->context, (XPointer*) &win) != 0) { + return; + } + + if (win->src.flashEnd) { + if ((win->src.flashEnd <= RGFW_linux_getTimeNS()) || RGFW_window_isInFocus(win)) { + RGFW_window_flash(win, RGFW_flashCancel); + } + } + + + /* + Repeated key presses are sent as a release followed by another press at the same time. + We want to convert that into a single key press event with the repeat flag set + */ + + RGFW_bool keyRepeat = RGFW_FALSE; + + if (E.type == KeyRelease && XEventsQueued(_RGFW->display, QueuedAfterReading)) { + XEvent NE; + XPeekEvent(_RGFW->display, &NE); + if (NE.type == KeyPress && E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) { + /* Use the next KeyPress event */ + XNextEvent(_RGFW->display, &E); + keyRepeat = RGFW_TRUE; + } + } + + switch (E.type) { + case KeyPress: { + if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return; + RGFW_key value = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode); + u8 sym = (u8)RGFW_rgfwToKeyChar(value); + + XkbStateRec state; + XkbGetState(_RGFW->display, XkbUseCoreKbd, &state); + RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask)); + + RGFW_keyCallback(win, value, sym, win->internal.mod, keyRepeat, RGFW_TRUE); + break; + } + case KeyRelease: { + if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return; + + RGFW_key value = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode); + u8 sym = (u8)RGFW_rgfwToKeyChar(value); + + XkbStateRec state; + XkbGetState(_RGFW->display, XkbUseCoreKbd, &state); + RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask)); + + RGFW_keyCallback(win, value, sym, win->internal.mod, keyRepeat, RGFW_FALSE); + break; + } + case ButtonPress: { + RGFW_bool scroll = RGFW_FALSE; + if (E.xbutton.button >= Button4 && E.xbutton.button <= 7) { + scroll = RGFW_TRUE; + } + + float scrollX = 0.0f; + float scrollY = 0.0f; + RGFW_mouseButton value = 0; + + switch (E.xbutton.button) { + case Button1: value = RGFW_mouseLeft; break; + case Button2: value = RGFW_mouseMiddle; break; + case Button3: value = RGFW_mouseRight; break; + case Button4: scrollY = 1.0; break; + case Button5: scrollY = -1.0; break; + case 6: scrollX = 1.0f; break; + case 7: scrollX = -1.0f; break; + default: + value = (u8)E.xbutton.button - Button1 - 4; + break; + } + + if (scroll) { + RGFW_mouseScrollCallback(win, scrollX, scrollY); + break; + } + + RGFW_mouseButtonCallback(win, value, RGFW_TRUE); + break; + } + case ButtonRelease: { + if (E.xbutton.button >= Button4 && E.xbutton.button <= 7) break; + + RGFW_mouseButton value = 0; + switch(E.xbutton.button) { + case Button1: value = RGFW_mouseLeft; break; + case Button2: value = RGFW_mouseMiddle; break; + case Button3: value = RGFW_mouseRight; break; + default: + value = (u8)E.xbutton.button - Button1 - 4; + break; + } + + RGFW_mouseButtonCallback(win, value, RGFW_FALSE); + break; + } + case MotionNotify: + RGFW_mousePosCallback(win, E.xmotion.x, E.xmotion.y, _RGFW->vectorX, _RGFW->vectorY); + break; + + case Expose: { + RGFW_windowRefreshCallback(win); + +#ifdef RGFW_ADVANCED_SMOOTH_RESIZE + XSyncValue value; + XSyncIntToValue(&value, (i32)win->src.counter_value); + XSyncSetCounter(_RGFW->display, win->src.counter, value); +#endif + break; + } + + case PropertyNotify: + if (E.xproperty.state != PropertyNewValue) break; + + if (E.xproperty.atom == WM_STATE) { + if (RGFW_window_isMinimized(win) && !(win->internal.flags & RGFW_windowMinimized)) { + RGFW_windowMinimizedCallback(win); + break; + } + } else if (E.xproperty.atom == _NET_WM_STATE) { + if (RGFW_window_isMaximized(win) && !(win->internal.flags & RGFW_windowMaximize)) { + RGFW_windowMaximizedCallback(win, win->x, win->y, win->w, win->h); + break; + } + } + + RGFW_window_checkMode(win); + break; + case MapNotify: case UnmapNotify: RGFW_window_checkMode(win); break; + case ClientMessage: { + RGFW_LOAD_ATOM(WM_DELETE_WINDOW); + /* if the client closed the window */ + if (E.xclient.data.l[0] == (long)WM_DELETE_WINDOW) { + RGFW_windowQuitCallback(win); + break; + } +#ifdef RGFW_ADVANCED_SMOOTH_RESIZE + if (E.xclient.message_type == WM_PROTOCOLS && (Atom)E.xclient.data.l[0] == _NET_WM_SYNC_REQUEST) { + RGFW_windowRefreshCallback(win); + win->src.counter_value = 0; + win->src.counter_value |= E.xclient.data.l[2]; + win->src.counter_value |= (E.xclient.data.l[3] << 32); + + XSyncValue value; + XSyncIntToValue(&value, (i32)win->src.counter_value); + XSyncSetCounter(_RGFW->display, win->src.counter, value); + break; + } +#endif + if ((win->internal.flags & RGFW_windowAllowDND) == 0) + return; + + i32 dragX = 0; + i32 dragY = 0; + + reply.xclient.window = source; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)win->src.window; + reply.xclient.data.l[1] = 0; + reply.xclient.data.l[2] = None; + + if (E.xclient.message_type == XdndEnter) { + if (version > 5) + break; + + unsigned long count; + Atom* formats; + Atom real_formats[6]; + Bool list = E.xclient.data.l[1] & 1; + + source = (unsigned long int)E.xclient.data.l[0]; + version = E.xclient.data.l[1] >> 24; + format = None; + if (list) { + Atom actualType; + i32 actualFormat; + unsigned long bytesAfter; + + XGetWindowProperty( + _RGFW->display, source, XdndTypeList, + 0, LONG_MAX, False, 4, + &actualType, &actualFormat, &count, &bytesAfter, (u8**)&formats + ); + } else { + count = 0; + + size_t i; + for (i = 2; i < 5; i++) { + if (E.xclient.data.l[i] != None) { + real_formats[count] = (unsigned long int)E.xclient.data.l[i]; + count += 1; + } + } + + formats = real_formats; + } + + Atom XtextPlain = XInternAtom(_RGFW->display, "text/plain", False); + Atom XtextUriList = XInternAtom(_RGFW->display, "text/uri-list", False); + + size_t i; + for (i = 0; i < count; i++) { + if (formats[i] == XtextUriList || formats[i] == XtextPlain) { + format = (int)formats[i]; + break; + } + } + + if (list) { + XFree(formats); + } + + break; + } + + if (E.xclient.message_type == XdndPosition) { + const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff; + const i32 yabs = (E.xclient.data.l[2]) & 0xffff; + Window dummy; + i32 xpos, ypos; + + if (version > 5) + break; + + XTranslateCoordinates( + _RGFW->display, XDefaultRootWindow(_RGFW->display), win->src.window, + xabs, yabs, &xpos, &ypos, &dummy + ); + + dragX = xpos; + dragY = ypos; + + reply.xclient.window = source; + reply.xclient.message_type = XdndStatus; + + if (format) { + reply.xclient.data.l[1] = 1; + if (version >= 2) + reply.xclient.data.l[4] = (long)XdndActionCopy; + } + + XSendEvent(_RGFW->display, source, False, NoEventMask, &reply); + XFlush(_RGFW->display); + break; + } + if (E.xclient.message_type != XdndDrop) + break; + + if (version > 5) + break; + + if (format) { + Time time = (version >= 1) + ? (Time)E.xclient.data.l[2] + : CurrentTime; + + XConvertSelection( + _RGFW->display, XdndSelection, (Atom)format, + XdndSelection, win->src.window, time + ); + } else if (version >= 2) { + XEvent new_reply = { ClientMessage }; + + XSendEvent(_RGFW->display, source, False, NoEventMask, &new_reply); + XFlush(_RGFW->display); + } + + RGFW_dataDragCallback(win, dragX, dragY); + } break; + case SelectionNotify: { + /* this is only for checking for xdnd drops */ + if (!(win->internal.enabledEvents & RGFW_dataDropFlag) || E.xselection.property != XdndSelection || !(win->internal.flags & RGFW_windowAllowDND)) + return; + char* data; + unsigned long result; + + Atom actualType; + i32 actualFormat; + unsigned long bytesAfter; + + XGetWindowProperty(_RGFW->display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data); + + if (result == 0) + break; + + const char* prefix = (const char*)"file://"; + + char* line; + + size_t count = 0; + char** files = _RGFW->files; + + while ((line = (char*)RGFW_strtok(data, "\r\n"))) { + char path[RGFW_MAX_PATH]; + + data = NULL; + + if (line[0] == '#') + continue; + + char* l; + for (l = line; 1; l++) { + if ((l - line) > 7) + break; + else if (*l != prefix[(l - line)]) + break; + else if (*l == '\0' && prefix[(l - line)] == '\0') { + line += 7; + while (*line != '/') + line++; + break; + } else if (*l == '\0') + break; + } + + count++; + + size_t index = 0; + while (*line) { + if (line[0] == '%' && line[1] && line[2]) { + char digits[3] = {0}; + digits[0] = line[1]; + digits[1] = line[2]; + digits[2] = '\0'; + path[index] = (char) RGFW_STRTOL(digits, NULL, 16); + line += 2; + } else + path[index] = *line; + + index++; + line++; + } + path[index] = '\0'; + RGFW_MEMCPY(files[count - 1], path, index + 1); + } + + RGFW_dataDropCallback(win, files, count); + if (data) + XFree(data); + + if (version >= 2) { + XEvent new_reply = { ClientMessage }; + new_reply.xclient.window = source; + new_reply.xclient.message_type = XdndFinished; + new_reply.xclient.format = 32; + new_reply.xclient.data.l[1] = (long int)result; + new_reply.xclient.data.l[2] = (long int)XdndActionCopy; + XSendEvent(_RGFW->display, source, False, NoEventMask, &new_reply); + XFlush(_RGFW->display); + } + break; + } + case FocusIn: + RGFW_focusCallback(win, 1); + break; + case FocusOut: + RGFW_focusCallback(win, 0); + break; + case EnterNotify: { + RGFW_mouseNotifyCallback(win, E.xcrossing.x, E.xcrossing.y, RGFW_TRUE); + break; + } + + case LeaveNotify: { + RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE); + break; + } + case ReparentNotify: + win->src.parent = E.xreparent.parent; + break; + case ConfigureNotify: { + /* detect resize */ + if (E.xconfigure.width != win->src.w || E.xconfigure.height != win->src.h) { + RGFW_window_checkMode(win); + win->src.w = E.xconfigure.width; + win->src.h = E.xconfigure.height; + RGFW_windowResizedCallback(win, E.xconfigure.width, E.xconfigure.height); + } + + i32 x = E.xconfigure.x; + i32 y = E.xconfigure.y; + + /* + if the event came from the server and we're not a direct child of the root window then + we're using local coords which need to be translated into screen coords + */ + Window root = DefaultRootWindow(_RGFW->display); + if (E.xany.send_event == 0 && win->src.parent != root) { + Window dummy = 0; + XTranslateCoordinates(_RGFW->display, win->src.parent, root, x, y, &x, &y, &dummy); + } + + /* detect move */ + if (E.xconfigure.x != win->src.x || E.xconfigure.y != win->src.y) { + win->src.x = E.xconfigure.x; + win->src.y = E.xconfigure.y; + RGFW_windowMovedCallback(win, E.xconfigure.x, E.xconfigure.y); + } + return; + } + default: + break; + } + + XFlush(_RGFW->display); +} + +void RGFW_FUNC(RGFW_pollEvents) (void) { + RGFW_resetPrevState(); + + XPending(_RGFW->display); + /* if there is no unread queued events, get a new one */ + while (QLength(_RGFW->display)) { + RGFW_XHandleEvent(); + } +} + +void RGFW_FUNC(RGFW_window_move) (RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + win->x = x; + win->y = y; + + XMoveWindow(_RGFW->display, win->src.window, x, y); + return; +} + + +void RGFW_FUNC(RGFW_window_resize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->w = (i32)w; + win->h = (i32)h; + + XResizeWindow(_RGFW->display, win->src.window, (u32)w, (u32)h); + + if ((win->internal.flags & RGFW_windowNoResize)) { + XSizeHints sh; + sh.flags = (1L << 4) | (1L << 5); + sh.min_width = sh.max_width = (i32)w; + sh.min_height = sh.max_height = (i32)h; + + XSetWMSizeHints(_RGFW->display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS); + } + return; +} + +void RGFW_FUNC(RGFW_window_setAspectRatio) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + + if (w == 0 && h == 0) + return; + XSizeHints hints; + long flags; + + XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags); + + hints.flags |= PAspect; + + hints.min_aspect.x = hints.max_aspect.x = (i32)w; + hints.min_aspect.y = hints.max_aspect.y = (i32)h; + + XSetWMNormalHints(_RGFW->display, win->src.window, &hints); + return; +} + +void RGFW_FUNC(RGFW_window_setMinSize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + long flags; + XSizeHints hints; + RGFW_MEMSET(&hints, 0, sizeof(XSizeHints)); + + XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags); + + hints.flags |= PMinSize; + + hints.min_width = (i32)w; + hints.min_height = (i32)h; + + XSetWMNormalHints(_RGFW->display, win->src.window, &hints); + return; +} + +void RGFW_FUNC(RGFW_window_setMaxSize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + long flags; + XSizeHints hints; + RGFW_MEMSET(&hints, 0, sizeof(XSizeHints)); + + XGetWMNormalHints(_RGFW->display, win->src.window, &hints, &flags); + + hints.flags |= PMaxSize; + + hints.max_width = (i32)w; + hints.max_height = (i32)h; + + XSetWMNormalHints(_RGFW->display, win->src.window, &hints); + return; +} + +void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized); +void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); + + XEvent xev = {0}; + xev.type = ClientMessage; + xev.xclient.window = win->src.window; + xev.xclient.message_type = _NET_WM_STATE; + xev.xclient.format = 32; + xev.xclient.data.l[0] = maximized; + xev.xclient.data.l[1] = (long int)_NET_WM_STATE_MAXIMIZED_HORZ; + xev.xclient.data.l[2] = (long int)_NET_WM_STATE_MAXIMIZED_VERT; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + + XSendEvent(_RGFW->display, DefaultRootWindow(_RGFW->display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); +} + +void RGFW_FUNC(RGFW_window_maximize) (RGFW_window* win) { + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + + RGFW_toggleXMaximized(win, 1); + return; +} + +void RGFW_FUNC(RGFW_window_focus) (RGFW_window* win) { + RGFW_ASSERT(win); + + XWindowAttributes attr; + XGetWindowAttributes(_RGFW->display, win->src.window, &attr); + if (attr.map_state != IsViewable) return; + + XSetInputFocus(_RGFW->display, win->src.window, RevertToPointerRoot, CurrentTime); + XFlush(_RGFW->display); +} + +void RGFW_FUNC(RGFW_window_raise) (RGFW_window* win) { + RGFW_ASSERT(win); + XMapRaised(_RGFW->display, win->src.window); + RGFW_window_setFullscreen(win, RGFW_window_isFullscreen(win)); +} + +void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen); +void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(_NET_WM_STATE); + + XEvent xev = {0}; + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.message_type = _NET_WM_STATE; + xev.xclient.window = win->src.window; + xev.xclient.format = 32; + xev.xclient.data.l[0] = fullscreen; + xev.xclient.data.l[1] = (long int)netAtom; + xev.xclient.data.l[2] = 0; + + XSendEvent(_RGFW->display, DefaultRootWindow(_RGFW->display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); +} + +void RGFW_FUNC(RGFW_window_setFullscreen)(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + + if (fullscreen) { + win->internal.flags |= RGFW_windowFullscreen; + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + } + else win->internal.flags &= ~(u32)RGFW_windowFullscreen; + + XRaiseWindow(_RGFW->display, win->src.window); + + RGFW_LOAD_ATOM(_NET_WM_STATE_FULLSCREEN); + RGFW_window_setXAtom(win, _NET_WM_STATE_FULLSCREEN, fullscreen); + + if (!(win->internal.flags & RGFW_windowTransparent)) { + const unsigned char value = fullscreen; + RGFW_LOAD_ATOM(_NET_WM_BYPASS_COMPOSITOR); + XChangeProperty( + _RGFW->display, win->src.window, + _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, + PropModeReplace, &value, 1); + } +} + +void RGFW_FUNC(RGFW_window_setFloating)(RGFW_window* win, RGFW_bool floating) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE); + RGFW_window_setXAtom(win, _NET_WM_STATE_ABOVE, floating); +} + +void RGFW_FUNC(RGFW_window_setOpacity)(RGFW_window* win, u8 opacity) { + RGFW_ASSERT(win != NULL); + const u32 value = (u32) (0xffffffffu * (double) opacity); + RGFW_LOAD_ATOM(NET_WM_WINDOW_OPACITY); + XChangeProperty(_RGFW->display, win->src.window, + NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1); +} + +void RGFW_FUNC(RGFW_window_minimize)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + if (RGFW_window_isMaximized(win)) return; + + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + XIconifyWindow(_RGFW->display, win->src.window, DefaultScreen(_RGFW->display)); + XFlush(_RGFW->display); +} + +void RGFW_FUNC(RGFW_window_restore)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_toggleXMaximized(win, RGFW_FALSE); + RGFW_window_move(win, win->internal.oldX, win->internal.oldY); + RGFW_window_resize(win, win->internal.oldW, win->internal.oldH); + + RGFW_window_show(win); + XFlush(_RGFW->display); +} + +RGFW_bool RGFW_FUNC(RGFW_window_isFloating)(RGFW_window* win) { + RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE); + + Atom actual_type; + int actual_format; + unsigned long nitems, bytes_after; + Atom* prop_return = NULL; + + int status = XGetWindowProperty(_RGFW->display, win->src.window, _NET_WM_STATE, 0, (~0L), False, XA_ATOM, + &actual_type, &actual_format, &nitems, &bytes_after, + (unsigned char **)&prop_return); + + if (status != Success || actual_type != XA_ATOM) + return RGFW_FALSE; + + unsigned long i; + for (i = 0; i < nitems; i++) + if (prop_return[i] == _NET_WM_STATE_ABOVE) return RGFW_TRUE; + + if (prop_return) + XFree(prop_return); + return RGFW_FALSE; +} + +void RGFW_FUNC(RGFW_window_setName)(RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + if (name == NULL) name = "\0"; + + XStoreName(_RGFW->display, win->src.window, name); + + RGFW_LOAD_ATOM(_NET_WM_NAME); RGFW_LOAD_ATOM(UTF8_STRING); + + XChangeProperty( + _RGFW->display, win->src.window, _NET_WM_NAME, UTF8_STRING, + 8, PropModeReplace, (u8*)name, (int)RGFW_unix_stringlen(name) + ); +} + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_FUNC(RGFW_window_setMousePassthrough) (RGFW_window* win, RGFW_bool passthrough) { + RGFW_ASSERT(win != NULL); + if (passthrough) { + Region region = XCreateRegion(); + XShapeCombineRegion(_RGFW->display, win->src.window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); + + return; + } + + XShapeCombineMask(_RGFW->display, win->src.window, ShapeInput, 0, 0, None, ShapeSet); +} +#endif /* RGFW_NO_PASSTHROUGH */ + +RGFW_bool RGFW_FUNC(RGFW_window_setIconEx) (RGFW_window* win, u8* data_src, i32 w, i32 h, RGFW_format format, RGFW_icon type) { + Atom _NET_WM_ICON = XInternAtom(_RGFW->display, "_NET_WM_ICON", False); + RGFW_ASSERT(win != NULL); + if (data_src == NULL) { + RGFW_bool res = (RGFW_bool)XChangeProperty( + _RGFW->display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, + PropModeReplace, (u8*)NULL, 0 + ); + return res; + } + + i32 count = (i32)(2 + (w * h)); + + unsigned long* data = (unsigned long*) RGFW_ALLOC((u32)count * sizeof(unsigned long)); + RGFW_ASSERT(data != NULL); + + RGFW_MEMSET(data, 0, (u32)count * sizeof(unsigned long)); + data[0] = (unsigned long)w; + data[1] = (unsigned long)h; + + RGFW_copyImageData64((u8*)&data[2], w, h, RGFW_formatBGRA8, data_src, format, RGFW_TRUE); + RGFW_bool res = RGFW_TRUE; + if (type & RGFW_iconTaskbar) { + res = (RGFW_bool)XChangeProperty( + _RGFW->display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, + PropModeReplace, (u8*)data, count + ); + } + + RGFW_copyImageData64((u8*)&data[2], w, h, RGFW_formatBGRA8, data_src, format, RGFW_FALSE); + + if (type & RGFW_iconWindow) { + XWMHints wm_hints; + wm_hints.flags = IconPixmapHint; + + i32 depth = DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display)); + XImage *image = XCreateImage(_RGFW->display, DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)), + (u32)depth, ZPixmap, 0, (char *)&data[2], (u32)w, (u32)h, 32, 0); + + wm_hints.icon_pixmap = XCreatePixmap(_RGFW->display, win->src.window, (u32)w, (u32)h, (u32)depth); + XPutImage(_RGFW->display, wm_hints.icon_pixmap, DefaultGC(_RGFW->display, DefaultScreen(_RGFW->display)), image, 0, 0, 0, 0, (u32)w, (u32)h); + image->data = NULL; + XDestroyImage(image); + + XSetWMHints(_RGFW->display, win->src.window, &wm_hints); + } + + RGFW_FREE(data); + XFlush(_RGFW->display); + return RGFW_BOOL(res); +} + +RGFW_mouse* RGFW_FUNC(RGFW_loadMouse) (u8* data, i32 w, i32 h, RGFW_format format) { + RGFW_ASSERT(data); +#ifndef RGFW_NO_X11_CURSOR + RGFW_init(); + XcursorImage* native = XcursorImageCreate((i32)w, (i32)h); + native->xhot = 0; + native->yhot = 0; + RGFW_MEMSET(native->pixels, 0, (u32)(w * h * 4)); + RGFW_copyImageData((u8*)native->pixels, w, h, RGFW_formatBGRA8, data, format); + + Cursor cursor = XcursorImageLoadCursor(_RGFW->display, native); + XcursorImageDestroy(native); + + return (void*)cursor; +#else + RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); + return NULL; +#endif +} + +void RGFW_FUNC(RGFW_window_setMouse)(RGFW_window* win, RGFW_mouse* mouse) { + RGFW_ASSERT(win && mouse); + XDefineCursor(_RGFW->display, win->src.window, (Cursor)mouse); +} + +void RGFW_FUNC(RGFW_freeMouse)(RGFW_mouse* mouse) { + RGFW_ASSERT(mouse); + XFreeCursor(_RGFW->display, (Cursor)mouse); +} + +void RGFW_FUNC(RGFW_window_moveMouse)(RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + + XEvent event; + XQueryPointer(_RGFW->display, DefaultRootWindow(_RGFW->display), + &event.xbutton.root, &event.xbutton.window, + &event.xbutton.x_root, &event.xbutton.y_root, + &event.xbutton.x, &event.xbutton.y, + &event.xbutton.state); + + win->internal.lastMouseX = x - win->x; + win->internal.lastMouseY = y - win->y; + if (event.xbutton.x == x && event.xbutton.y == y) + return; + + XWarpPointer(_RGFW->display, None, win->src.window, 0, 0, 0, 0, (int) x - win->x, (int) y - win->y); +} + +RGFW_bool RGFW_FUNC(RGFW_window_setMouseDefault) (RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} + +RGFW_bool RGFW_FUNC(RGFW_window_setMouseStandard) (RGFW_window* win, u8 mouse) { + RGFW_ASSERT(win != NULL); + + u32 mouseIcon = 0; + + switch (mouse) { + case RGFW_mouseNormal: mouseIcon = XC_left_ptr; break; + case RGFW_mouseArrow: mouseIcon = XC_left_ptr; break; + case RGFW_mouseIbeam: mouseIcon = XC_xterm; break; + case RGFW_mouseWait: mouseIcon = XC_watch; break; + case RGFW_mouseCrosshair: mouseIcon = XC_tcross; break; + case RGFW_mouseProgress: mouseIcon = XC_watch; break; + case RGFW_mouseResizeNWSE: mouseIcon = XC_top_left_corner; break; + case RGFW_mouseResizeNESW: mouseIcon = XC_top_right_corner; break; + case RGFW_mouseResizeEW: mouseIcon = XC_sb_h_double_arrow; break; + case RGFW_mouseResizeNS: mouseIcon = XC_sb_v_double_arrow; break; + case RGFW_mouseResizeNW: mouseIcon = XC_top_left_corner; break; + case RGFW_mouseResizeN: mouseIcon = XC_top_side; break; + case RGFW_mouseResizeNE: mouseIcon = XC_top_right_corner; break; + case RGFW_mouseResizeE: mouseIcon = XC_right_side; break; + case RGFW_mouseResizeSE: mouseIcon = XC_bottom_right_corner; break; + case RGFW_mouseResizeS: mouseIcon = XC_bottom_side; break; + case RGFW_mouseResizeSW: mouseIcon = XC_bottom_left_corner; break; + case RGFW_mouseResizeW: mouseIcon = XC_left_side; break; + case RGFW_mouseResizeAll: mouseIcon = XC_fleur; break; + case RGFW_mouseNotAllowed: mouseIcon = XC_pirate; break; + case RGFW_mousePointingHand: mouseIcon = XC_hand2; break; + default: return RGFW_FALSE; + } + + Cursor cursor = XCreateFontCursor(_RGFW->display, mouseIcon); + XDefineCursor(_RGFW->display, win->src.window, (Cursor) cursor); + XFreeCursor(_RGFW->display, (Cursor) cursor); + return RGFW_TRUE; +} + +void RGFW_FUNC(RGFW_window_hide)(RGFW_window* win) { + win->internal.flags |= (u32)RGFW_windowHide; + XUnmapWindow(_RGFW->display, win->src.window); + + XFlush(_RGFW->display); +} + +void RGFW_FUNC(RGFW_window_show) (RGFW_window* win) { + win->internal.flags &= ~(u32)RGFW_windowHide; + if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); + + if (RGFW_window_isHidden(win) == RGFW_FALSE) { + return; + } + + XMapWindow(_RGFW->display, win->src.window); + RGFW_window_move(win, win->x, win->y); + + RGFW_waitForShowEvent_X11(win); + RGFW_window_setFullscreen(win, RGFW_window_isFullscreen(win)); + return; +} + +void RGFW_FUNC(RGFW_window_flash) (RGFW_window* win, RGFW_flashRequest request) { + if (RGFW_window_isInFocus(win) && request) { + return; + } + + XWMHints* wmhints = XGetWMHints(_RGFW->display, win->src.window); + if (wmhints == NULL) return; + + if (request) { + wmhints->flags |= XUrgencyHint; + if (request == RGFW_flashBriefly) + win->src.flashEnd = RGFW_linux_getTimeNS() + (u64)1e+9; + if (request == RGFW_flashUntilFocused) + win->src.flashEnd = (u64)-1; + } else { + win->src.flashEnd = 0; + wmhints->flags &= ~XUrgencyHint; + } + + XSetWMHints(_RGFW->display, win->src.window, wmhints); + XFree(wmhints); +} + +RGFW_ssize_t RGFW_FUNC(RGFW_readClipboardPtr)(char* str, size_t strCapacity) { + RGFW_init(); + RGFW_LOAD_ATOM(XSEL_DATA); RGFW_LOAD_ATOM(UTF8_STRING); RGFW_LOAD_ATOM(CLIPBOARD); + if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) == _RGFW->helperWindow) { + if (str != NULL) + RGFW_STRNCPY(str, _RGFW->clipboard, _RGFW->clipboard_len - 1); + _RGFW->clipboard[_RGFW->clipboard_len - 1] = '\0'; + return (RGFW_ssize_t)_RGFW->clipboard_len - 1; + } + + XEvent event; + int format; + unsigned long N, sizeN; + char* data; + Atom target; + + XConvertSelection(_RGFW->display, CLIPBOARD, UTF8_STRING, XSEL_DATA, _RGFW->helperWindow, CurrentTime); + XSync(_RGFW->display, 0); + while (1) { + XNextEvent(_RGFW->display, &event); + if (event.type != SelectionNotify) continue; + + if (event.xselection.selection != CLIPBOARD || event.xselection.property == 0) + return -1; + break; + } + + XGetWindowProperty(event.xselection.display, event.xselection.requestor, + event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, + &format, &sizeN, &N, (u8**) &data); + + RGFW_ssize_t size; + if (sizeN > strCapacity && str != NULL) + size = -1; + + if ((target == UTF8_STRING || target == XA_STRING) && str != NULL) { + RGFW_MEMCPY(str, data, sizeN); + str[sizeN] = '\0'; + XFree(data); + } else if (str != NULL) size = -1; + + XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); + size = (RGFW_ssize_t)sizeN; + + return size; +} + +i32 RGFW_XHandleClipboardSelectionHelper(void) { + RGFW_LOAD_ATOM(SAVE_TARGETS); + + XEvent event; + XPending(_RGFW->display); + + if (QLength(_RGFW->display) || XEventsQueued(_RGFW->display, QueuedAlready) + XEventsQueued(_RGFW->display, QueuedAfterReading)) + XNextEvent(_RGFW->display, &event); + else + return 0; + + switch (event.type) { + case SelectionRequest: + RGFW_XHandleClipboardSelection(&event); + return 0; + case SelectionNotify: + if (event.xselection.target == SAVE_TARGETS) + return 0; + break; + default: break; + } + + return 0; +} + +void RGFW_FUNC(RGFW_writeClipboard)(const char* text, u32 textLen) { + RGFW_LOAD_ATOM(SAVE_TARGETS); RGFW_LOAD_ATOM(CLIPBOARD); + RGFW_init(); + + /* request ownership of the clipboard section and request to convert it, this means its our job to convert it */ + XSetSelectionOwner(_RGFW->display, CLIPBOARD, _RGFW->helperWindow, CurrentTime); + if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) != _RGFW->helperWindow) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, "X11 failed to become owner of clipboard selection"); + return; + } + + if (_RGFW->clipboard) + RGFW_FREE(_RGFW->clipboard); + + _RGFW->clipboard = (char*)RGFW_ALLOC(textLen); + RGFW_ASSERT(_RGFW->clipboard != NULL); + + RGFW_STRNCPY(_RGFW->clipboard, text, textLen - 1); + _RGFW->clipboard[textLen - 1] = '\0'; + _RGFW->clipboard_len = textLen; + return; +} + +RGFW_bool RGFW_FUNC(RGFW_window_isHidden)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + XWindowAttributes windowAttributes; + XGetWindowAttributes(_RGFW->display, win->src.window, &windowAttributes); + + return (windowAttributes.map_state != IsViewable); +} + +RGFW_bool RGFW_FUNC(RGFW_window_isMinimized)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(WM_STATE); + + Atom actual_type; + i32 actual_format; + unsigned long nitems, bytes_after; + unsigned char* prop_data; + + i32 status = XGetWindowProperty(_RGFW->display, win->src.window, WM_STATE, 0, 2, False, + AnyPropertyType, &actual_type, &actual_format, + &nitems, &bytes_after, &prop_data); + + if (status == Success && nitems >= 1 && prop_data == (unsigned char*)IconicState) { + XFree(prop_data); + return RGFW_TRUE; + } + + if (prop_data != NULL) + XFree(prop_data); + + XWindowAttributes windowAttributes; + XGetWindowAttributes(_RGFW->display, win->src.window, &windowAttributes); + return windowAttributes.map_state != IsViewable; +} + +RGFW_bool RGFW_FUNC(RGFW_window_isMaximized)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); + + Atom actual_type; + i32 actual_format; + unsigned long nitems, bytes_after; + unsigned char* prop_data; + + i32 status = XGetWindowProperty(_RGFW->display, win->src.window, _NET_WM_STATE, 0, 1024, False, + XA_ATOM, &actual_type, &actual_format, + &nitems, &bytes_after, &prop_data); + + if (status != Success) { + if (prop_data != NULL) + XFree(prop_data); + + return RGFW_FALSE; + } + + u64 i; + for (i = 0; i < nitems; ++i) { + if (prop_data[i] == _NET_WM_STATE_MAXIMIZED_VERT || + prop_data[i] == _NET_WM_STATE_MAXIMIZED_HORZ) { + XFree(prop_data); + return RGFW_TRUE; + } + } + + if (prop_data != NULL) + XFree(prop_data); + + return RGFW_FALSE; +} + +static float XGetSystemContentDPI(Display* display, i32 screen) { + float dpi = 96.0f; + + #ifndef RGFW_NO_DPI + RGFW_UNUSED(screen); + char* rms = XResourceManagerString(display); + XrmDatabase db = NULL; + if (rms) db = XrmGetStringDatabase(rms); + + if (rms && db) { + XrmValue value; + char* type = NULL; + + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0) + dpi = (float)RGFW_ATOF(value.addr); + XrmDestroyDatabase(db); + } + #else + dpi = RGFW_ROUND(DisplayWidth(display, screen) / (DisplayWidthMM(display, screen) / 25.4)); + #endif + + return dpi; +} + +RGFW_monitor RGFW_XCreateMonitor(i32 screen); +RGFW_monitor RGFW_XCreateMonitor(i32 screen) { + RGFW_monitor monitor; + RGFW_init(); + + Display* display = _RGFW->display; + + if (screen == -1) screen = DefaultScreen(display); + + Screen* scrn = DefaultScreenOfDisplay(display); + + monitor.x = 0; + monitor.y = 0; + monitor.mode.w = scrn->width; + monitor.mode.h = scrn->height; + monitor.physW = (float)DisplayWidthMM(display, screen) / 25.4f; + monitor.physH = (float)DisplayHeightMM(display, screen) / 25.4f; + + RGFW_splitBPP((u32)DefaultDepth(display, screen), &monitor.mode); + + char* name = XDisplayName((const char*)display); + RGFW_STRNCPY(monitor.name, name, sizeof(monitor.name) - 1); + monitor.name[sizeof(monitor.name) - 1] = '\0'; + + float dpi = XGetSystemContentDPI(display, screen); + monitor.pixelRatio = dpi >= 192.0f ? 2 : 1.0f; + monitor.scaleX = (float) (dpi) / 96.0f; + monitor.scaleY = (float) (dpi) / 96.0f; + + #ifndef RGFW_NO_DPI + XRRCrtcInfo* ci = NULL; + XRRScreenResources* sr = NULL; + + { + XRRScreenConfiguration* conf = XRRGetScreenInfo(display, RootWindow(display, screen)); + monitor.mode.refreshRate = (u32)XRRConfigCurrentRate(conf); + + sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen)); + int crtc = screen; + + if (sr->ncrtc > crtc) { + ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]); + } + + XRRFreeScreenConfigInfo(conf); + } + #endif + + #ifndef RGFW_NO_DPI + XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]); + + if (info == NULL || ci == NULL) { + XRRFreeScreenResources(sr); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, "monitor found"); + return monitor; + } + + + float physW = (float)info->mm_width / 25.4f; + float physH = (float)info->mm_height / 25.4f; + + RGFW_STRNCPY(monitor.name, info->name, sizeof(monitor.name) - 1); + monitor.name[sizeof(monitor.name) - 1] = '\0'; + + XRRFreeOutputInfo(info); + info = NULL; + + if (physW > 0.0f && physH > 0.0f) { + monitor.physW = physW; + monitor.physH = physH; + } + + monitor.x = ci->x; + monitor.y = ci->y; + + if (ci->width && ci->height) { + monitor.mode.w = (i32)ci->width; + monitor.mode.h = (i32)ci->height; + } + #endif + + #ifndef RGFW_NO_DPI + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + #endif + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, "monitor found"); + return monitor; +} + +RGFW_monitor* RGFW_FUNC(RGFW_getMonitors)(size_t* len) { + static RGFW_monitor monitors[7]; + RGFW_init(); + + Display* display = _RGFW->display; + i32 max = ScreenCount(display); + + i32 i; + for (i = 0; i < max && i < RGFW_MAX_MONITORS; i++) + monitors[i] = RGFW_XCreateMonitor(i); + + if (len != NULL) *len = (size_t)((max <= RGFW_MAX_MONITORS) ? (max) : (RGFW_MAX_MONITORS)); + + return monitors; +} + +RGFW_monitor RGFW_FUNC(RGFW_getPrimaryMonitor)(void) { + return RGFW_XCreateMonitor(-1); +} + +RGFW_bool RGFW_FUNC(RGFW_monitor_requestMode)(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { + #ifndef RGFW_NO_DPI + RGFW_init(); + XRRScreenConfiguration *conf = XRRGetScreenInfo(_RGFW->display, DefaultRootWindow(_RGFW->display)); + XRRScreenResources* screenRes = XRRGetScreenResources(_RGFW->display, DefaultRootWindow(_RGFW->display)); + if (screenRes == NULL) return RGFW_FALSE; + + int i; + for (i = 0; i < screenRes->ncrtc; i++) { + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(_RGFW->display, screenRes, screenRes->crtcs[i]); + if (!crtcInfo) continue; + + if (mon.x == crtcInfo->x && mon.y == crtcInfo->y && (u32)mon.mode.w == crtcInfo->width && (u32)mon.mode.h == crtcInfo->height) { + RRMode rmode = None; + int index; + for (index = 0; index < screenRes->nmode; index++) { + RGFW_monitorMode foundMode; + foundMode.w = (i32)screenRes->modes[index].width; + foundMode.h = (i32)screenRes->modes[index].height; + foundMode.refreshRate = (u32)XRRConfigCurrentRate(conf); + RGFW_splitBPP((u32)DefaultDepth(_RGFW->display, DefaultScreen(_RGFW->display)), &foundMode); + + if (RGFW_monitorModeCompare(mode, foundMode, request)) { + rmode = screenRes->modes[index].id; + + RROutput output = screenRes->outputs[i]; + XRROutputInfo* info = XRRGetOutputInfo(_RGFW->display, screenRes, output); + if (info) { + XRRSetCrtcConfig(_RGFW->display, screenRes, screenRes->crtcs[i], + CurrentTime, 0, 0, rmode, RR_Rotate_0, &output, 1); + XRRFreeOutputInfo(info); + XRRFreeCrtcInfo(crtcInfo); + XRRFreeScreenResources(screenRes); + return RGFW_TRUE; + } + } + } + + XRRFreeCrtcInfo(crtcInfo); + XRRFreeScreenResources(screenRes); + return RGFW_FALSE; + } + + XRRFreeCrtcInfo(crtcInfo); + } + + XRRFreeScreenResources(screenRes); + XRRFreeScreenConfigInfo(conf); +#endif + return RGFW_FALSE; +} + +RGFW_monitor RGFW_FUNC(RGFW_window_getMonitor) (RGFW_window* win) { + RGFW_monitor mon; + RGFW_MEMSET(&mon, 0, sizeof(mon)); + + RGFW_ASSERT(win != NULL); + + XWindowAttributes attrs; + if (!XGetWindowAttributes(_RGFW->display, win->src.window, &attrs)) { + return mon; + } + + i32 i; + for (i = 0; i < ScreenCount(_RGFW->display) && i < RGFW_MAX_MONITORS; i++) { + Screen* screen = ScreenOfDisplay(_RGFW->display, i); + if (attrs.x >= 0 && attrs.x < XWidthOfScreen(screen) && + attrs.y >= 0 && attrs.y < XHeightOfScreen(screen)) + return RGFW_XCreateMonitor(i); + } + return mon; + +} + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_FUNC(RGFW_window_createContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* context, RGFW_glHints* hints) { + /* for checking extensions later */ + const char sRGBARBstr[] = "GLX_ARB_framebuffer_sRGB"; + const char sRGBEXTstr[] = "GLX_EXT_framebuffer_sRGB"; + const char noErorrStr[] = "GLX_ARB_create_context_no_error"; + const char flushStr[] = "GLX_ARB_context_flush_control"; + const char robustStr[] = "GLX_ARB_create_context_robustness"; + + /* basic RGFW int */ + win->src.ctx.native = context; + win->src.gfxType = RGFW_gfxNativeOpenGL; + + /* This is required so that way the user can create their own OpenGL context after RGFW_createWindow is used */ + RGFW_bool showWindow = RGFW_FALSE; + if (win->src.window) { + showWindow = (RGFW_window_isMinimized(win) == RGFW_FALSE); + RGFW_window_closePlatform(win); + } + + RGFW_bool transparent = (win->internal.flags & RGFW_windowTransparent); + + /* start by creating a GLX config / X11 Viusal */ + XVisualInfo visual; + GLXFBConfig bestFbc; + + i32 visual_attribs[40]; + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, visual_attribs, 40); + RGFW_attribStack_pushAttribs(&stack, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR); + RGFW_attribStack_pushAttribs(&stack, GLX_X_RENDERABLE, 1); + RGFW_attribStack_pushAttribs(&stack, GLX_RENDER_TYPE, GLX_RGBA_BIT); + RGFW_attribStack_pushAttribs(&stack, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT); + RGFW_attribStack_pushAttribs(&stack, GLX_DOUBLEBUFFER, 1); + RGFW_attribStack_pushAttribs(&stack, GLX_ALPHA_SIZE, hints->alpha); + RGFW_attribStack_pushAttribs(&stack, GLX_DEPTH_SIZE, hints->depth); + RGFW_attribStack_pushAttribs(&stack, GLX_STENCIL_SIZE, hints->stencil); + RGFW_attribStack_pushAttribs(&stack, GLX_STEREO, hints->stereo); + RGFW_attribStack_pushAttribs(&stack, GLX_AUX_BUFFERS, hints->auxBuffers); + RGFW_attribStack_pushAttribs(&stack, GLX_RED_SIZE, hints->red); + RGFW_attribStack_pushAttribs(&stack, GLX_GREEN_SIZE, hints->green); + RGFW_attribStack_pushAttribs(&stack, GLX_BLUE_SIZE, hints->blue); + RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_RED_SIZE, hints->accumRed); + RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_GREEN_SIZE, hints->accumGreen); + RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_BLUE_SIZE, hints->accumBlue); + RGFW_attribStack_pushAttribs(&stack, GLX_ACCUM_ALPHA_SIZE, hints->accumAlpha); + + if (hints->sRGB) { + if (RGFW_extensionSupportedPlatform_OpenGL(sRGBARBstr, sizeof(sRGBARBstr))) + RGFW_attribStack_pushAttribs(&stack, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, hints->sRGB); + if (RGFW_extensionSupportedPlatform_OpenGL(sRGBEXTstr, sizeof(sRGBEXTstr))) + RGFW_attribStack_pushAttribs(&stack, GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, hints->sRGB); + } + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + + /* find the configs */ + i32 fbcount; + GLXFBConfig* fbc = glXChooseFBConfig(_RGFW->display, DefaultScreen(_RGFW->display), visual_attribs, &fbcount); + + i32 best_fbc = -1; + i32 best_depth = 0; + i32 best_samples = 0; + + if (fbcount == 0) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to find any valid GLX visual configs."); + return 0; + } + + /* search through all found configs to find the best match */ + i32 i; + for (i = 0; i < fbcount; i++) { + XVisualInfo* vi = glXGetVisualFromFBConfig(_RGFW->display, fbc[i]); + if (vi == NULL) + continue; + + i32 samp_buf, samples; + glXGetFBConfigAttrib(_RGFW->display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(_RGFW->display, fbc[i], GLX_SAMPLES, &samples); + + if (best_fbc == -1) best_fbc = i; + if ((!(transparent) || vi->depth == 32) && best_depth == 0) { + best_fbc = i; + best_depth = vi->depth; + } + if ((!(transparent) || vi->depth == 32) && samples <= hints->samples && samples > best_samples) { + best_fbc = i; + best_depth = vi->depth; + best_samples = samples; + } + XFree(vi); + } + + if (best_fbc == -1) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to get a valid GLX visual."); + return 0; + } + + /* we found a config */ + bestFbc = fbc[best_fbc]; + XVisualInfo* vi = glXGetVisualFromFBConfig(_RGFW->display, bestFbc); + if (vi->depth != 32 && transparent) + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to to find a matching visual with a 32-bit depth."); + + if (best_samples < hints->samples) + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Failed to load a matching sample count."); + + XFree(fbc); + visual = *vi; + XFree(vi); + + /* use the visual to create a new window */ + RGFW_XCreateWindow(visual, "", win->internal.flags, win); + + if (showWindow) { + RGFW_window_show(win); + } + + /* create the actual OpenGL context */ + i32 context_attribs[40]; + RGFW_attribStack_init(&stack, context_attribs, 40); + + i32 mask = 0; + switch (hints->profile) { + case RGFW_glES: mask |= GLX_CONTEXT_ES_PROFILE_BIT_EXT; break; + case RGFW_glForwardCompatibility: mask |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; break; + case RGFW_glCompatibility: mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; break; + case RGFW_glCore: mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; break; + default: mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; break; + } + + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_PROFILE_MASK_ARB, mask); + + if (hints->minor || hints->major) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_MAJOR_VERSION_ARB, hints->major); + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_MINOR_VERSION_ARB, hints->minor); + } + + + if (RGFW_extensionSupportedPlatform_OpenGL(flushStr, sizeof(flushStr))) { + if (hints->releaseBehavior == RGFW_glReleaseFlush) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + } else if (hints->releaseBehavior == RGFW_glReleaseNone) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + } + } + + i32 flags = 0; + if (hints->debug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + if (hints->robustness && RGFW_extensionSupportedPlatform_OpenGL(robustStr, sizeof(robustStr))) flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; + if (flags) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_FLAGS_ARB, flags); + } + + if (RGFW_extensionSupportedPlatform_OpenGL(noErorrStr, sizeof(noErorrStr))) { + RGFW_attribStack_pushAttribs(&stack, GLX_CONTEXT_OPENGL_NO_ERROR_ARB, hints->noError); + } + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + + /* create the context */ + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; + char str[] = "glXCreateContextAttribsARB"; + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddressARB((u8*) str); + + GLXContext ctx = NULL; + if (hints->share) { + ctx = hints->share->ctx; + } + + if (glXCreateContextAttribsARB == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load proc address 'glXCreateContextAttribsARB', loading a generic OpenGL context."); + win->src.ctx.native->ctx = glXCreateContext(_RGFW->display, &visual, ctx, True); + } else { + _RGFW->x11Error = NULL; + win->src.ctx.native->ctx = glXCreateContextAttribsARB(_RGFW->display, bestFbc, ctx, True, context_attribs); + if (_RGFW->x11Error || win->src.ctx.native->ctx == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an OpenGL context with AttribsARB, loading a generic OpenGL context."); + win->src.ctx.native->ctx = glXCreateContext(_RGFW->display, &visual, ctx, True); + } + } + + #ifndef RGFW_NO_GLXWINDOW + win->src.ctx.native->window = glXCreateWindow(_RGFW->display, bestFbc, win->src.window, NULL); + #else + win->src.ctx.native->window = win->src.window; + #endif + + glXMakeCurrent(_RGFW->display, (Drawable)win->src.ctx.native->window, (GLXContext)win->src.ctx.native->ctx); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized."); + + RGFW_window_swapInterval_OpenGL(win, 0); + + return RGFW_TRUE; +} + +void RGFW_FUNC(RGFW_window_deleteContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* ctx) { + #ifndef RGFW_NO_GLXWINDOW + if (win->src.ctx.native->window != win->src.window) { + glXDestroyWindow(_RGFW->display, win->src.ctx.native->window); + } + #endif + + glXDestroyContext(_RGFW->display, ctx->ctx); + win->src.ctx.native = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed."); +} + +RGFW_bool RGFW_FUNC(RGFW_extensionSupportedPlatform_OpenGL)(const char * extension, size_t len) { + RGFW_init(); + const char* extensions = glXQueryExtensionsString(_RGFW->display, XDefaultScreen(_RGFW->display)); + return (extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len); +} + +RGFW_proc RGFW_FUNC(RGFW_getProcAddress_OpenGL)(const char* procname) { return glXGetProcAddress((u8*) procname); } + +void RGFW_FUNC(RGFW_window_makeCurrentContext_OpenGL) (RGFW_window* win) { if (win) RGFW_ASSERT(win->src.ctx.native); + if (win == NULL) + glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL); + else + glXMakeCurrent(_RGFW->display, (Drawable)win->src.ctx.native->window, (GLXContext) win->src.ctx.native->ctx); + return; +} +void* RGFW_FUNC(RGFW_getCurrentContext_OpenGL) (void) { return glXGetCurrentContext(); } +void RGFW_FUNC(RGFW_window_swapBuffers_OpenGL) (RGFW_window* win) { RGFW_ASSERT(win->src.ctx.native); glXSwapBuffers(_RGFW->display, win->src.ctx.native->window); } + +void RGFW_FUNC(RGFW_window_swapInterval_OpenGL) (RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); + /* cached pfn to avoid calling glXGetProcAddress more than once */ + static PFNGLXSWAPINTERVALEXTPROC pfn = NULL; + static int (*pfn2)(int) = NULL; + + if (pfn == NULL) { + u8 str[] = "glXSwapIntervalEXT"; + pfn = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress(str); + if (pfn == NULL) { + pfn = (PFNGLXSWAPINTERVALEXTPROC)1; + const char* array[] = {"GLX_MESA_swap_control", "GLX_SGI_swap_control"}; + + size_t i; + for (i = 0; i < sizeof(array) / sizeof(char*) && pfn2 == NULL; i++) { + pfn2 = (int(*)(int))glXGetProcAddress((u8*)array[i]); + } + + if (pfn2 != NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function, fallingback to the native swapinterval function"); + } else { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function"); + } + } + } + + if (pfn != (PFNGLXSWAPINTERVALEXTPROC)1) { + pfn(_RGFW->display, win->src.ctx.native->window, swapInterval); + } + else if (pfn2 != NULL) { + pfn2(swapInterval); + } +} +#endif /* RGFW_OPENGL */ + +i32 RGFW_initPlatform_X11(void) { + #ifdef RGFW_USE_XDL + XDL_init(); + #endif + + #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + #if defined(__CYGWIN__) + RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor-1.so"); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so"); + #else + RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so.1"); + #endif + RGFW_PROC_DEF(X11Cursorhandle, XcursorImageCreate); + RGFW_PROC_DEF(X11Cursorhandle, XcursorImageDestroy); + RGFW_PROC_DEF(X11Cursorhandle, XcursorImageLoadCursor); + #endif + + #if !defined(RGFW_NO_X11_XI_PRELOAD) + #if defined(__CYGWIN__) + RGFW_LOAD_LIBRARY(X11Xihandle, "libXi-6.so"); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so"); + #else + RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so.6"); + #endif + RGFW_PROC_DEF(X11Xihandle, XISelectEvents); + #endif + + #if !defined(RGFW_NO_X11_EXT_PRELOAD) + #if defined(__CYGWIN__) + RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext-6.so"); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so"); + #else + RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so.6"); + #endif + RGFW_PROC_DEF(X11XEXThandle, XSyncCreateCounter); + RGFW_PROC_DEF(X11XEXThandle, XSyncIntToValue); + RGFW_PROC_DEF(X11XEXThandle, XSyncSetCounter); + RGFW_PROC_DEF(X11XEXThandle, XShapeCombineRegion); + RGFW_PROC_DEF(X11XEXThandle, XShapeCombineMask); + #endif + + XInitThreads(); /*!< init X11 threading */ + _RGFW->display = XOpenDisplay(0); + _RGFW->context = XUniqueContext(); + + XSetWindowAttributes wa; + RGFW_MEMSET(&wa, 0, sizeof(wa)); + wa.event_mask = PropertyChangeMask; + _RGFW->helperWindow = XCreateWindow(_RGFW->display, XDefaultRootWindow(_RGFW->display), 0, 0, 1, 1, 0, 0, + InputOnly, DefaultVisual(_RGFW->display, DefaultScreen(_RGFW->display)), CWEventMask, &wa); + + u8 RGFW_blk[] = { 0, 0, 0, 0 }; + _RGFW->hiddenMouse = RGFW_loadMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8); + _RGFW->clipboard = NULL; + + XkbComponentNamesRec rec; + XkbDescPtr desc = XkbGetMap(_RGFW->display, 0, XkbUseCoreKbd); + XkbDescPtr evdesc; + XSetErrorHandler(RGFW_XErrorHandler); + u8 old[256]; + + XkbGetNames(_RGFW->display, XkbKeyNamesMask, desc); + + RGFW_MEMSET(&rec, 0, sizeof(rec)); + char evdev[] = "evdev"; + rec.keycodes = evdev; + evdesc = XkbGetKeyboardByName(_RGFW->display, XkbUseCoreKbd, &rec, XkbGBN_KeyNamesMask, XkbGBN_KeyNamesMask, False); + /* memo: RGFW_keycodes[x11 keycode] = rgfw keycode */ + if(evdesc != NULL && desc != NULL) { + int i, j; + for(i = 0; i < (int)sizeof(old); i++){ + old[i] = _RGFW->keycodes[i]; + _RGFW->keycodes[i] = 0; + } + for(i = evdesc->min_key_code; i <= evdesc->max_key_code; i++){ + for(j = desc->min_key_code; j <= desc->max_key_code; j++){ + if(RGFW_STRNCMP(evdesc->names->keys[i].name, desc->names->keys[j].name, XkbKeyNameLength) == 0){ + _RGFW->keycodes[j] = old[i]; + break; + } + } + } + XkbFreeKeyboard(desc, 0, True); + XkbFreeKeyboard(evdesc, 0, True); + } + + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; + XISetMask(mask, XI_RawMotion); + + XIEventMask em; + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + + XISelectEvents(_RGFW->display, XDefaultRootWindow(_RGFW->display), &em, 1); + + return 0; +} + +void RGFW_deinitPlatform_X11(void) { + #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL; + /* to save the clipboard on the x server after the window is closed */ + RGFW_LOAD_ATOM(CLIPBOARD_MANAGER); RGFW_LOAD_ATOM(CLIPBOARD); + RGFW_LOAD_ATOM(SAVE_TARGETS); + if (XGetSelectionOwner(_RGFW->display, CLIPBOARD) == _RGFW->helperWindow) { + XConvertSelection(_RGFW->display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, _RGFW->helperWindow, CurrentTime); + while (RGFW_XHandleClipboardSelectionHelper()); + } + if (_RGFW->clipboard) { + RGFW_FREE(_RGFW->clipboard); + _RGFW->clipboard = NULL; + } + + if (_RGFW->hiddenMouse) { + RGFW_freeMouse(_RGFW->hiddenMouse); + _RGFW->hiddenMouse = NULL; + } + + XDestroyWindow(_RGFW->display, (Drawable) _RGFW->helperWindow); /*!< close the window */ + XCloseDisplay(_RGFW->display); /*!< kill connection to the x server */ + + #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) + RGFW_FREE_LIBRARY(X11Cursorhandle); + #endif + #if !defined(RGFW_NO_X11_XI_PRELOAD) + RGFW_FREE_LIBRARY(X11Xihandle); + #endif + + #ifdef RGFW_USE_XDL + XDL_close(); + #endif + + #if !defined(RGFW_NO_X11_EXT_PRELOAD) + RGFW_FREE_LIBRARY(X11XEXThandle); + #endif +} + +void RGFW_FUNC(RGFW_window_closePlatform)(RGFW_window* win) { + XFreeGC(_RGFW->display, win->src.gc); + XDeleteContext(_RGFW->display, win->src.window, _RGFW->context); + XDestroyWindow(_RGFW->display, (Drawable) win->src.window); /*!< close the window */ + return; +} + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_FUNC(RGFW_window_createSurface_WebGPU) (RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + WGPUSurfaceSourceXlibWindow fromXlib = {0}; + fromXlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow; + fromXlib.display = _RGFW->display; + fromXlib.window = window->src.window; + + surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromXlib.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#endif + +#endif +/* + End of X11 linux / wayland / unix defines +*/ + +/* + + Start of Wayland defayland +*/ + +#ifdef RGFW_WAYLAND +#ifdef RGFW_X11 +#undef RGFW_FUNC /* remove previous define */ +#define RGFW_FUNC(func) func##_Wayland +#else +#define RGFW_FUNC(func) func +#endif + +/* +Wayland TODO: (out of date) +- fix RGFW_keyPressed lock state + + RGFW_windowMoved, the window was moved (by the user) + RGFW_windowRefresh The window content needs to be refreshed + + RGFW_dataDrop a file has been dropped into the window + RGFW_dataDrag + +- window args: + #define RGFW_windowNoResize the window cannot be resized by the user + #define RGFW_windowAllowDND the window supports drag and drop + #define RGFW_scaleToMonitor scale the window to the screen + +- other missing functions functions ("TODO wayland") (~30 functions) +- fix buffer rendering weird behavior +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct wl_display* RGFW_getDisplay_Wayland(void) { return _RGFW->wl_display; } +struct wl_surface* RGFW_window_getWindow_Wayland(RGFW_window* win) { return win->src.surface; } + + +/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ +#include "xdg-shell.h" +#include "xdg-toplevel-icon-v1.h" +#include "xdg-decoration-unstable-v1.h" +#include "relative-pointer-unstable-v1.h" +#include "pointer-constraints-unstable-v1.h" +#include "xdg-output-unstable-v1.h" + + +void RGFW_toggleWaylandMaximized(RGFW_window* win, RGFW_bool maximized); + +static void RGFW_wl_setOpaque(RGFW_window* win) { + struct wl_region* wl_region = wl_compositor_create_region(_RGFW->compositor); + + if (!wl_region) return; /* return if no region was created */ + + wl_region_add(wl_region, 0, 0, win->w, win->h); + wl_surface_set_opaque_region(win->src.surface, wl_region); + wl_region_destroy(wl_region); + +} + +static void RGFW_wl_xdg_wm_base_ping_handler(void* data, struct xdg_wm_base* wm_base, + u32 serial) { + RGFW_UNUSED(data); + xdg_wm_base_pong(wm_base, serial); +} +static void RGFW_wl_xdg_surface_configure_handler(void* data, struct xdg_surface* xdg_surface, + u32 serial) { + + xdg_surface_ack_configure(xdg_surface, serial); + + RGFW_window* win = (RGFW_window*)data; + + if (win == NULL) { + win = _RGFW->kbOwner; + if (win == NULL) + return; + } + + /* useful for libdecor */ + if (win->src.activated != win->src.pending_activated) { + win->src.activated = win->src.pending_activated; + } + + if (win->src.maximized != win->src.pending_maximized) { + RGFW_toggleWaylandMaximized(win, win->src.pending_maximized); + + RGFW_window_checkMode(win); + } + + + if (win->src.resizing) { + + /* Do not create a resize event if the window is maximized */ + if (!win->src.maximized) { + RGFW_windowResizedCallback(win, win->w, win->h); + } + RGFW_window_resize(win, win->w, win->h); + if (!(win->internal.flags & RGFW_windowTransparent)) { + RGFW_wl_setOpaque(win); + } + } + +} + +static void RGFW_wl_xdg_toplevel_configure_handler(void* data, struct xdg_toplevel* toplevel, + i32 width, i32 height, struct wl_array* states) { + + RGFW_UNUSED(toplevel); + RGFW_window* win = (RGFW_window*)data; + + + win->src.pending_activated = RGFW_FALSE; + win->src.pending_maximized = RGFW_FALSE; + win->src.resizing = RGFW_FALSE; + + + enum xdg_toplevel_state* state; + wl_array_for_each(state, states) { + switch (*state) { + case XDG_TOPLEVEL_STATE_ACTIVATED: + win->src.pending_activated = RGFW_TRUE; + break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: + win->src.pending_maximized = RGFW_TRUE; + break; + default: + break; + } + + } + /* if width and height are not zero and are not the same as the window */ + /* the window is resizing so update the values */ + if ((width && height) && (win->w != width || win->h != height)) { + win->src.resizing = RGFW_TRUE; + win->src.w = win->w = width; + win->src.h = win->h = height; + } +} + +static void RGFW_wl_xdg_toplevel_close_handler(void* data, struct xdg_toplevel *toplevel) { + RGFW_UNUSED(toplevel); + RGFW_window* win = (RGFW_window*)data; + + if (!win->internal.shouldClose) { + RGFW_windowQuitCallback(win); + } +} + +static void RGFW_wl_xdg_decoration_configure_handler(void* data, + struct zxdg_toplevel_decoration_v1* zxdg_toplevel_decoration_v1, u32 mode) { + RGFW_window* win = (RGFW_window*)data; RGFW_UNUSED(zxdg_toplevel_decoration_v1); + + /* this is expected to run once */ + /* set the decoration mode set by earlier request */ + if (mode != win->src.decoration_mode) { + win->src.decoration_mode = mode; + } +} + +static void RGFW_wl_shm_format_handler(void* data, struct wl_shm *shm, u32 format) { + RGFW_UNUSED(data); RGFW_UNUSED(shm); RGFW_UNUSED(format); +} + +static void RGFW_wl_relative_pointer_motion(void *data, struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, + u32 time_hi, u32 time_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) { + + RGFW_UNUSED(zwp_relative_pointer_v1); RGFW_UNUSED(time_hi); RGFW_UNUSED(time_lo); + RGFW_UNUSED(dx_unaccel); RGFW_UNUSED(dy_unaccel); + + RGFW_info* RGFW = (RGFW_info*)data; + + RGFW_ASSERT(RGFW->mouseOwner != NULL); + RGFW_window* win = RGFW->mouseOwner; + + RGFW_ASSERT(win); + + float vecX = (float)wl_fixed_to_double(dx); + float vecY = (float)wl_fixed_to_double(dy); + RGFW_mousePosCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, vecX, vecY); +} + +static void RGFW_wl_pointer_locked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) { + RGFW_UNUSED(zwp_locked_pointer_v1); + RGFW_info* RGFW = (RGFW_info*)data; + wl_pointer_set_cursor(RGFW->wl_pointer, RGFW->mouse_enter_serial, NULL, 0, 0); /* draw no cursor */ +} + +static void RGFW_wl_pointer_enter(void* data, struct wl_pointer* pointer, u32 serial, + struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + + /* save when the pointer is locked or using default cursor */ + RGFW->mouse_enter_serial = serial; + win->internal.mouseInside = RGFW_TRUE; + RGFW->windowState.mouseEnter = RGFW_TRUE; + + RGFW->mouseOwner = win; + + /* set the cursor */ + if (win->src.using_custom_cursor) { + wl_pointer_set_cursor(pointer, serial, win->src.custom_cursor_surface, 0, 0); + } + else { + RGFW_window_setMouseDefault(win); + } + + i32 x = (i32)wl_fixed_to_double(surface_x); + i32 y = (i32)wl_fixed_to_double(surface_y); + RGFW_mouseNotifyCallback(win, x, y, RGFW_TRUE); +} + +static void RGFW_wl_pointer_leave(void* data, struct wl_pointer *pointer, u32 serial, struct wl_surface *surface) { + RGFW_UNUSED(pointer); RGFW_UNUSED(serial); + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + RGFW_info* RGFW = (RGFW_info*)data; + if (RGFW->mouseOwner == win) + RGFW->mouseOwner = NULL; + + RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE); +} + +static void RGFW_wl_pointer_motion(void* data, struct wl_pointer *pointer, u32 time, wl_fixed_t x, wl_fixed_t y) { + RGFW_UNUSED(pointer); RGFW_UNUSED(time); + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_ASSERT(RGFW->mouseOwner != NULL); + + RGFW_window* win = RGFW->mouseOwner; + + i32 convertedX = (i32)wl_fixed_to_double(x); + i32 convertedY = (i32)wl_fixed_to_double(y); + float newVecX = (float)(convertedX - win->internal.lastMouseX); + float newVecY = (float)(convertedY - win->internal.lastMouseY); + + RGFW_mousePosCallback(win, convertedX, convertedY, newVecX, newVecY); +} + +static void RGFW_wl_pointer_button(void* data, struct wl_pointer *pointer, u32 serial, u32 time, u32 button, u32 state) { + RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial); + RGFW_info* RGFW = (RGFW_info*)data; + + RGFW_ASSERT(RGFW->mouseOwner != NULL); + RGFW_window* win = RGFW->mouseOwner; + + u32 b = (button - 0x110); + + /* flip right and middle button codes */ + if (b == 1) b = 2; + else if (b == 2) b = 1; + + RGFW_mouseButtonCallback(win, (u8)b, RGFW_BOOL(state)); +} + +static void RGFW_wl_pointer_axis(void* data, struct wl_pointer *pointer, u32 time, u32 axis, wl_fixed_t value) { + RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis); + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_ASSERT(RGFW->mouseOwner != NULL); + RGFW_window* win = RGFW->mouseOwner; + + float scrollX = 0.0; + float scrollY = 0.0; + + if (!(win->internal.enabledEvents & (RGFW_BIT(RGFW_mouseScroll)))) return; + + if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) + scrollX = (float)(-wl_fixed_to_double(value) / 10.0); + else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) + scrollY = (float)(-wl_fixed_to_double(value) / 10.0); + + RGFW_mouseScrollCallback(win, scrollX, scrollY); +} + + +static void RGFW_doNothing(void) { } + +static void RGFW_wl_keyboard_keymap(void* data, struct wl_keyboard *keyboard, u32 format, i32 fd, u32 size) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(format); + RGFW_info* RGFW = (RGFW_info*)data; + + char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); + xkb_keymap_unref(RGFW->keymap); + RGFW->keymap = xkb_keymap_new_from_string(RGFW->xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + + munmap(keymap_string, size); + close(fd); + xkb_state_unref(RGFW->xkb_state); + RGFW->xkb_state = xkb_state_new(RGFW->keymap); +} + +static void RGFW_wl_keyboard_enter(void* data, struct wl_keyboard *keyboard, u32 serial, struct wl_surface *surface, struct wl_array *keys) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(keys); + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + RGFW->kbOwner = win; + + + // this is to prevent race conditions + if (RGFW->data_device != NULL && win->src.data_source != NULL) { + wl_data_device_set_selection(RGFW->data_device, win->src.data_source, serial); + } + /* is set when RGFW_window_minimize is called; if the minimize button is */ + /* pressed this flag is not set since there is no event to listen for */ + if (win->src.minimized == RGFW_TRUE) win->src.minimized = RGFW_FALSE; + + RGFW_focusCallback(win, RGFW_TRUE); +} + +static void RGFW_wl_keyboard_leave(void* data, struct wl_keyboard *keyboard, u32 serial, struct wl_surface *surface) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + if (RGFW->kbOwner == win) + RGFW->kbOwner = NULL; + + RGFW_focusCallback(win, RGFW_FALSE); +} + +static void RGFW_wl_keyboard_key(void* data, struct wl_keyboard *keyboard, u32 serial, u32 time, u32 key, u32 state) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); + + RGFW_info* RGFW = (RGFW_info*)data; + if (RGFW->kbOwner == NULL) return; + + RGFW_window *RGFW_key_win = RGFW->kbOwner; + xkb_keysym_t keysym = xkb_state_key_get_one_sym(RGFW->xkb_state, key + 8); + + u32 RGFWkey = RGFW_apiKeyToRGFW(key + 8); + + RGFW_updateKeyMods(RGFW_key_win, RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "Lock")), RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "Mod2")), RGFW_BOOL(xkb_keymap_mod_get_index(RGFW->keymap, "ScrollLock"))); + RGFW_keyCallback(RGFW_key_win, (u8)RGFWkey, (u8)keysym, RGFW_key_win->internal.mod, RGFW_window_isKeyDown(RGFW_key_win, (u8)RGFWkey), RGFW_BOOL(state)); +} + +static void RGFW_wl_keyboard_modifiers(void* data, struct wl_keyboard *keyboard, u32 serial, u32 mods_depressed, u32 mods_latched, u32 mods_locked, u32 group) { + RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); + RGFW_info* RGFW = (RGFW_info*)data; + xkb_state_update_mask(RGFW->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} + +static void RGFW_wl_seat_capabilities(void* data, struct wl_seat *seat, u32 capabilities) { + RGFW_info* RGFW = (RGFW_info*)data; + static struct wl_pointer_listener pointer_listener; + RGFW_MEMSET(&pointer_listener, 0, sizeof(pointer_listener)); + pointer_listener.enter = &RGFW_wl_pointer_enter; + pointer_listener.leave = &RGFW_wl_pointer_leave; + pointer_listener.motion = &RGFW_wl_pointer_motion; + pointer_listener.button = &RGFW_wl_pointer_button; + pointer_listener.axis = &RGFW_wl_pointer_axis; + + static struct wl_keyboard_listener keyboard_listener; + RGFW_MEMSET(&keyboard_listener, 0, sizeof(keyboard_listener)); + keyboard_listener.keymap = &RGFW_wl_keyboard_keymap; + keyboard_listener.enter = &RGFW_wl_keyboard_enter; + keyboard_listener.leave = &RGFW_wl_keyboard_leave; + keyboard_listener.key = &RGFW_wl_keyboard_key; + keyboard_listener.modifiers = &RGFW_wl_keyboard_modifiers; + + if ((capabilities & WL_SEAT_CAPABILITY_POINTER) && !RGFW->wl_pointer) { + RGFW->wl_pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(RGFW->wl_pointer, &pointer_listener, RGFW); + } + if ((capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && !RGFW->wl_keyboard) { + RGFW->wl_keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(RGFW->wl_keyboard, &keyboard_listener, RGFW); + } + + if (!(capabilities & WL_SEAT_CAPABILITY_POINTER) && RGFW->wl_pointer) { + wl_pointer_destroy(RGFW->wl_pointer); + } + if (!(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) && RGFW->wl_keyboard) { + wl_keyboard_destroy(RGFW->wl_keyboard); + } +} + +static void RGFW_wl_output_set_geometry(void *data, struct wl_output *wl_output, + i32 x, i32 y, i32 physical_width, i32 physical_height, + i32 subpixel, const char *make, const char *model, i32 transform) { + + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + monitor->x = x; + monitor->y = y; + + monitor->physW = (float)physical_width / 25.4f; + monitor->physH = (float)physical_height / 25.4f; + + RGFW_UNUSED(wl_output); + RGFW_UNUSED(subpixel); + RGFW_UNUSED(make); + RGFW_UNUSED(model); + RGFW_UNUSED(transform); +} + +static void RGFW_wl_output_set_mode(void *data, struct wl_output *wl_output, u32 flags, + i32 width, i32 height, i32 refresh) { + + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + + monitor->mode.w = width; + monitor->mode.h = height; + monitor->mode.refreshRate = (u32)RGFW_ROUND( ((float)refresh / 1000) ); + RGFW_UNUSED(width); + RGFW_UNUSED(height); + RGFW_UNUSED(wl_output); + RGFW_UNUSED(flags); +} + +static void RGFW_wl_output_set_scale(void *data, struct wl_output *wl_output, i32 factor) { + /* this is for pixelRatio */ + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + + monitor->pixelRatio = (float)factor; + RGFW_UNUSED(wl_output); +} + +static void RGFW_wl_output_set_name(void *data, struct wl_output *wl_output, const char *name) { + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + + RGFW_STRNCPY(monitor->name, name, sizeof(monitor->name) - 1); + monitor->name[sizeof(monitor->name) - 1] = '\0'; + + RGFW_UNUSED(wl_output); + +} + +static void RGFW_xdg_output_logical_pos(void *data, struct zxdg_output_v1 *zxdg_output_v1, i32 x, i32 y) { + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + monitor->x = x; + monitor->y = y; + RGFW_UNUSED(zxdg_output_v1); +} + +static void RGFW_xdg_output_logical_size(void *data, struct zxdg_output_v1 *zxdg_output_v1, i32 width, i32 height) { + RGFW_monitor* monitor = &((RGFW_monitorNode*)data)->mon; + + float mon_float_width = (float) monitor->mode.w; + float mon_float_height = (float) monitor->mode.h; + + monitor->scaleX = (mon_float_width / (float) width); + monitor->scaleY = (mon_float_height / (float) height); + + /* under xwayland the monitor changes w & h when compositor scales it */ + monitor->mode.w = width; + monitor->mode.h = height; + RGFW_UNUSED(zxdg_output_v1); +} + +static void RGFW_wl_create_outputs(struct wl_registry *const registry, u32 id) { + struct wl_output *output = wl_registry_bind(registry, id, &wl_output_interface, wl_display_get_version(_RGFW->wl_display) < 4 ? 3 : 4); + RGFW_monitorNode* node; + RGFW_monitor mon; + + if (!output) return; + + char RGFW_mon_default_name[10]; + + RGFW_SNPRINTF(RGFW_mon_default_name, sizeof(RGFW_mon_default_name), "monitor-%li", _RGFW->monitors.count); + RGFW_STRNCPY(mon.name, RGFW_mon_default_name, sizeof(mon.name) - 1); + mon.name[sizeof(mon.name) - 1] = '\0'; + + /* set in case compositor does not send one */ + /* or no xdg_output support */ + mon.scaleY = mon.scaleX = mon.pixelRatio = 1.0f; + + node = RGFW_monitors_add(mon); + if (node == NULL) return; + + node->id = id; + node->output = output; + + static const struct wl_output_listener wl_output_listener = { + .geometry = RGFW_wl_output_set_geometry, + .mode = RGFW_wl_output_set_mode, + .done = (void (*)(void *,struct wl_output *))&RGFW_doNothing, + .scale = RGFW_wl_output_set_scale, + .name = RGFW_wl_output_set_name, + .description = (void (*)(void *, struct wl_output *, const char *))&RGFW_doNothing + }; + + /* the wl_output will have a reference to the node */ + wl_output_set_user_data(output, node); + + /* pass the monitor so we can access it in the callback functions */ + wl_output_add_listener(output, &wl_output_listener, node); + + if (!_RGFW->xdg_output_manager) return; /* compositor does not support it */ + + static const struct zxdg_output_v1_listener xdg_output_listener = { + .name = (void (*)(void *,struct zxdg_output_v1 *, const char *))&RGFW_doNothing, + .done = (void (*)(void *,struct zxdg_output_v1 *))&RGFW_doNothing, + .description = (void (*)(void *,struct zxdg_output_v1 *, const char *))&RGFW_doNothing, + .logical_position = RGFW_xdg_output_logical_pos, + .logical_size = RGFW_xdg_output_logical_size + }; + + node->xdg_output = zxdg_output_manager_v1_get_xdg_output(_RGFW->xdg_output_manager, node->output); + zxdg_output_v1_add_listener(node->xdg_output, &xdg_output_listener, node); +} + +static void RGFW_wl_surface_enter(void *data, struct wl_surface *wl_surface, struct wl_output *output) { + RGFW_UNUSED(wl_surface); + + RGFW_window* win = (RGFW_window*)data; + RGFW_monitorNode* node = wl_output_get_user_data(output); + win->src.active_monitor = node->mon; + + #ifndef RGFW_NO_MONITOR + if (win->internal.flags & RGFW_windowScaleToMonitor) + RGFW_window_scaleToMonitor(win); + #endif +} + +static void RGFW_wl_data_source_send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, i32 fd) { + RGFW_UNUSED(data); RGFW_UNUSED(wl_data_source); + + // a client can accept our clipboard + if (RGFW_STRNCMP(mime_type, "text/plain;charset=utf-8", 25) == 0) { + // do not write \0 + write(fd, _RGFW->clipboard, _RGFW->clipboard_len - 1); + } + + close(fd); +} + +static void RGFW_wl_data_source_cancelled(void *data, struct wl_data_source *wl_data_source) { + + RGFW_info* RGFW = (RGFW_info*)data; + + if (RGFW->kbOwner->src.data_source == wl_data_source) { + RGFW->kbOwner->src.data_source = NULL; + } + + wl_data_source_destroy(wl_data_source); + +} + +static void RGFW_wl_data_device_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *wl_data_offer) { + + RGFW_UNUSED(data); RGFW_UNUSED(wl_data_device); + static const struct wl_data_offer_listener wl_data_offer_listener = { + .offer = (void (*)(void *data, struct wl_data_offer *wl_data_offer, const char *))RGFW_doNothing, + .source_actions = (void (*)(void *data, struct wl_data_offer *wl_data_offer, u32 dnd_action))RGFW_doNothing, + .action = (void (*)(void *data, struct wl_data_offer *wl_data_offer, u32 dnd_action))RGFW_doNothing + }; + wl_data_offer_add_listener(wl_data_offer, &wl_data_offer_listener, NULL); +} + +static void RGFW_wl_data_device_selection(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *wl_data_offer) { + RGFW_UNUSED(data); RGFW_UNUSED(wl_data_device); + /* Clipboard is empty */ + if (wl_data_offer == NULL) { + return; + } + + int pfds[2]; + pipe(pfds); + + wl_data_offer_receive(wl_data_offer, "text/plain;charset=utf-8", pfds[1]); + close(pfds[1]); + + wl_display_roundtrip(_RGFW->wl_display); + + char buf[1024]; + + ssize_t n = read(pfds[0], buf, sizeof(buf)); + + _RGFW->clipboard = (char*)RGFW_ALLOC((size_t)n); + RGFW_ASSERT(_RGFW->clipboard != NULL); + RGFW_STRNCPY(_RGFW->clipboard, buf, (size_t)n); + + _RGFW->clipboard_len = (size_t)n + 1; + + close(pfds[0]); + + wl_data_offer_destroy(wl_data_offer); + +} + +static void RGFW_wl_global_registry_handler(void* data, struct wl_registry *registry, u32 id, const char *interface, u32 version) { + + static struct wl_seat_listener seat_listener = {&RGFW_wl_seat_capabilities, (void (*)(void *, struct wl_seat *, const char *))&RGFW_doNothing}; + static const struct wl_shm_listener shm_listener = { .format = RGFW_wl_shm_format_handler }; + + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_UNUSED(version); + + if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) { + RGFW->compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 4); + } else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) { + RGFW->xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1); + } else if (RGFW_STRNCMP(interface, zxdg_decoration_manager_v1_interface.name, 255) == 0) { + RGFW->decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, zwp_pointer_constraints_v1_interface.name, 255) == 0) { + RGFW->constraint_manager = wl_registry_bind(registry, id, &zwp_pointer_constraints_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, zwp_relative_pointer_manager_v1_interface.name, 255) == 0) { + RGFW->relative_pointer_manager = wl_registry_bind(registry, id, &zwp_relative_pointer_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, xdg_toplevel_icon_manager_v1_interface.name, 255) == 0) { + RGFW->icon_manager = wl_registry_bind(registry, id, &xdg_toplevel_icon_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) { + RGFW->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); + wl_shm_add_listener(RGFW->shm, &shm_listener, RGFW); + } else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) { + RGFW->seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); + wl_seat_add_listener(RGFW->seat, &seat_listener, RGFW); + } else if (RGFW_STRNCMP(interface, zxdg_output_manager_v1_interface.name, 255) == 0) { + RGFW->xdg_output_manager = wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface,"wl_output", 10) == 0) { + RGFW_wl_create_outputs(registry, id); + } else if (RGFW_STRNCMP(interface,"wl_data_device_manager", 23) == 0) { + RGFW->data_device_manager = wl_registry_bind(registry, id, &wl_data_device_manager_interface, 1); + } +} + +static void RGFW_wl_global_registry_remove(void* data, struct wl_registry *registry, u32 id) { + RGFW_UNUSED(data); RGFW_UNUSED(registry); + RGFW_info* RGFW = (RGFW_info*)data; + RGFW_monitorNode* prev = RGFW->monitors.list.head; + RGFW_monitorNode* node = NULL; + if (prev == NULL) return; + + if (prev->id != id) { + /* find the first node that has a matching id */ + while(prev->next != NULL && prev->next->id != id) { + prev = prev->next; + } + + if (prev->next == NULL) return; + node = prev->next; + } else { + node = prev; + } + + if (node->output) { + wl_output_destroy(node->output); + } + + if (node->xdg_output) { + zxdg_output_v1_destroy(node->xdg_output); + } + + RGFW_monitors_remove(node, prev); +} + +static void RGFW_wl_randname(char *buf) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + + int i; + for (i = 0; i < 6; ++i) { + buf[i] = (char)('A'+(r&15)+(r&16)*2); + r >>= 5; + } +} + +static int RGFW_wl_anonymous_shm_open(void) { + char name[] = "/RGFW-wayland-XXXXXX"; + int retries = 100; + + do { + RGFW_wl_randname(name + RGFW_unix_stringlen(name) - 6); + + --retries; + /* shm_open guarantees that O_CLOEXEC is set */ + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + + return -1; +} + +static int RGFW_wl_create_shm_file(off_t size) { + int fd = RGFW_wl_anonymous_shm_open(); + if (fd < 0) { + return fd; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} + +i32 RGFW_initPlatform_Wayland(void) { + _RGFW->wl_display = wl_display_connect(NULL); + if (_RGFW->wl_display == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, "Failed to load Wayland display"); + return -1; + } + + _RGFW->compositor = NULL; + static const struct wl_registry_listener registry_listener = { + .global = RGFW_wl_global_registry_handler, + .global_remove = RGFW_wl_global_registry_remove, + }; + + _RGFW->registry = wl_display_get_registry(_RGFW->wl_display); + wl_registry_add_listener(_RGFW->registry, ®istry_listener, _RGFW); + + wl_display_roundtrip(_RGFW->wl_display); /* bind to globals */ + + if (_RGFW->compositor == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, "Can't find compositor."); + return 1; + } + + if (_RGFW->wl_cursor_theme == NULL) { + _RGFW->wl_cursor_theme = wl_cursor_theme_load(NULL, 24, _RGFW->shm); + _RGFW->cursor_surface = wl_compositor_create_surface(_RGFW->compositor); + } + + u8 RGFW_blk[] = { 0, 0, 0, 0 }; + _RGFW->hiddenMouse = RGFW_loadMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8); + + static const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = RGFW_wl_xdg_wm_base_ping_handler, + }; + + xdg_wm_base_add_listener(_RGFW->xdg_wm_base, &xdg_wm_base_listener, NULL); + + _RGFW->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + static const struct wl_data_device_listener wl_data_device_listener = { + .data_offer = RGFW_wl_data_device_data_offer, + .enter = (void (*)(void *, struct wl_data_device *, u32, struct wl_surface*, wl_fixed_t, wl_fixed_t, struct wl_data_offer *))&RGFW_doNothing, + .leave = (void (*)(void *, struct wl_data_device *))&RGFW_doNothing, + .motion = (void (*)(void *, struct wl_data_device *, u32, wl_fixed_t, wl_fixed_t))&RGFW_doNothing, + .drop = (void (*)(void *, struct wl_data_device *))&RGFW_doNothing, + .selection = RGFW_wl_data_device_selection + }; + + if (_RGFW->seat && _RGFW->data_device_manager) { + _RGFW->data_device = wl_data_device_manager_get_data_device(_RGFW->data_device_manager, _RGFW->seat); + wl_data_device_add_listener(_RGFW->data_device, &wl_data_device_listener, NULL); + } + + return 0; +} + +void RGFW_deinitPlatform_Wayland(void) { + if (_RGFW->clipboard) { + RGFW_FREE(_RGFW->clipboard); + _RGFW->clipboard = NULL; + } + + if (_RGFW->wl_pointer) { + wl_pointer_destroy(_RGFW->wl_pointer); + } + if (_RGFW->wl_keyboard) { + wl_keyboard_destroy(_RGFW->wl_keyboard); + } + + wl_registry_destroy(_RGFW->registry); + if (_RGFW->decoration_manager != NULL) + zxdg_decoration_manager_v1_destroy(_RGFW->decoration_manager); + if (_RGFW->relative_pointer_manager != NULL) { + zwp_relative_pointer_manager_v1_destroy(_RGFW->relative_pointer_manager); + } + + if (_RGFW->relative_pointer) { + zwp_relative_pointer_v1_destroy(_RGFW->relative_pointer); + } + + if (_RGFW->constraint_manager != NULL) { + zwp_pointer_constraints_v1_destroy(_RGFW->constraint_manager); + } + + if (_RGFW->xdg_output_manager != NULL) + if (_RGFW->icon_manager != NULL) { + xdg_toplevel_icon_manager_v1_destroy(_RGFW->icon_manager); + } + + if (_RGFW->xdg_output_manager) { + zxdg_output_manager_v1_destroy(_RGFW->xdg_output_manager); + } + + if (_RGFW->data_device_manager) { + wl_data_device_manager_destroy(_RGFW->data_device_manager); + } + + if (_RGFW->data_device) { + wl_data_device_destroy(_RGFW->data_device); + } + + if (_RGFW->wl_cursor_theme != NULL) { + wl_cursor_theme_destroy(_RGFW->wl_cursor_theme); + } + + RGFW_freeMouse(_RGFW->hiddenMouse); + + RGFW_monitorNode* node = _RGFW->monitors.list.head; + + while (node != NULL) { + if (node->output) { + wl_output_destroy(node->output); + } + + if (node->xdg_output) { + zxdg_output_v1_destroy(node->xdg_output); + } + + _RGFW->monitors.count -= 1; + node = node->next; + + } + + wl_surface_destroy(_RGFW->cursor_surface); + wl_shm_destroy(_RGFW->shm); + wl_seat_release(_RGFW->seat); + xdg_wm_base_destroy(_RGFW->xdg_wm_base); + wl_compositor_destroy(_RGFW->compositor); + wl_display_disconnect(_RGFW->wl_display); +} + +RGFW_bool RGFW_FUNC(RGFW_createSurfacePtr) (u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoBuffer, "Creating a 4 channel buffer"); + + u32 size = (u32)(surface->w * surface->h * 4); + int fd = RGFW_wl_create_shm_file(size); + if (fd < 0) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to create a buffer."); + return RGFW_FALSE; + } + + surface->native.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (surface->native.buffer == MAP_FAILED) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "mmap failed."); + return RGFW_FALSE; + } + + struct wl_shm_pool* pool = wl_shm_create_pool(_RGFW->shm, fd, (i32)size); + surface->native.wl_buffer = wl_shm_pool_create_buffer(pool, 0, (i32)surface->w, (i32)surface->h, (i32)surface->w * 4, WL_SHM_FORMAT_ARGB8888); + wl_shm_pool_destroy(pool); + + close(fd); + + surface->native.format = RGFW_formatBGRA8; + return RGFW_TRUE; +} + +void RGFW_FUNC(RGFW_window_blitSurface) (RGFW_window* win, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + RGFW_copyImageData(surface->native.buffer, win->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format); + + wl_surface_attach(win->src.surface, surface->native.wl_buffer, 0, 0); + wl_surface_damage(win->src.surface, 0, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h)); + wl_surface_commit(win->src.surface); +} + +void RGFW_FUNC(RGFW_surface_freePtr) (RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + wl_buffer_destroy(surface->native.wl_buffer); + munmap(surface->native.buffer, (size_t)(surface->w * surface->h * 4)); +} + +void RGFW_FUNC(RGFW_window_setBorder) (RGFW_window* win, RGFW_bool border) { + RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border); + + /* for now just toggle between SSD & CSD depending on the bool */ + if (_RGFW->decoration_manager != NULL) { + zxdg_toplevel_decoration_v1_set_mode(win->src.decoration, (border ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE : ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE)); + } +} + +void RGFW_FUNC(RGFW_window_setRawMouseModePlatform) (RGFW_window* win, RGFW_bool state) { + RGFW_ASSERT(win); + if (_RGFW->relative_pointer_manager == NULL) return; + + if (state == RGFW_FALSE) { + if (_RGFW->relative_pointer != NULL) + zwp_relative_pointer_v1_destroy(_RGFW->relative_pointer); + _RGFW->relative_pointer = NULL; + return; + } + + if (_RGFW->relative_pointer != NULL) return; + + _RGFW->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(_RGFW->relative_pointer_manager, _RGFW->wl_pointer); + + static const struct zwp_relative_pointer_v1_listener relative_motion_listener = { + .relative_motion = RGFW_wl_relative_pointer_motion + }; + + zwp_relative_pointer_v1_add_listener(_RGFW->relative_pointer, &relative_motion_listener, _RGFW); +} + +void RGFW_FUNC(RGFW_window_captureMousePlatform) (RGFW_window* win, RGFW_bool state) { + RGFW_ASSERT(win); + + /* compositor has no support or window already is locked do nothing */ + if (_RGFW->constraint_manager == NULL) return; + + if (state == RGFW_FALSE) { + if (win->src.locked_pointer != NULL) + zwp_locked_pointer_v1_destroy(win->src.locked_pointer); + win->src.locked_pointer = NULL; + return; + } + + + if (win->src.locked_pointer != NULL) return; + win->src.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(_RGFW->constraint_manager, win->src.surface, _RGFW->wl_pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + + static const struct zwp_locked_pointer_v1_listener locked_listener = { + .locked = RGFW_wl_pointer_locked, + .unlocked = (void (*)(void *, struct zwp_locked_pointer_v1 *))RGFW_doNothing + }; + + zwp_locked_pointer_v1_add_listener(win->src.locked_pointer, &locked_listener, _RGFW); +} + +RGFW_window* RGFW_FUNC(RGFW_createWindowPlatform) (const char* name, RGFW_windowFlags flags, RGFW_window* win) { + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, "RGFW Wayland support is experimental"); + + static const struct xdg_surface_listener xdg_surface_listener = { + .configure = RGFW_wl_xdg_surface_configure_handler, + }; + + static const struct wl_surface_listener wl_surface_listener = { + .enter = RGFW_wl_surface_enter, + .leave = (void (*)(void *, struct wl_surface *, struct wl_output *))&RGFW_doNothing, + .preferred_buffer_scale = (void (*)(void *, struct wl_surface *, i32))&RGFW_doNothing, + .preferred_buffer_transform = (void (*)(void *, struct wl_surface *, u32))&RGFW_doNothing + }; + + win->src.surface = wl_compositor_create_surface(_RGFW->compositor); + wl_surface_add_listener(win->src.surface, &wl_surface_listener, win); + + /* create a surface for a custom cursor */ + win->src.custom_cursor_surface = wl_compositor_create_surface(_RGFW->compositor); + + win->src.xdg_surface = xdg_wm_base_get_xdg_surface(_RGFW->xdg_wm_base, win->src.surface); + xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, win); + + xdg_wm_base_set_user_data(_RGFW->xdg_wm_base, win); + + win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface); + + xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->w, win->h); + + if (!(win->internal.flags & RGFW_windowTransparent)) { /* no transparency */ + RGFW_wl_setOpaque(win); + } + + static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = RGFW_wl_xdg_toplevel_configure_handler, + .close = RGFW_wl_xdg_toplevel_close_handler, + }; + + xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, win); + + /* compositor supports both SSD & CSD + So choose accordingly + */ + if (_RGFW->decoration_manager) { + u32 decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; + win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + _RGFW->decoration_manager, win->src.xdg_toplevel); + + static const struct zxdg_toplevel_decoration_v1_listener xdg_decoration_listener = { + .configure = RGFW_wl_xdg_decoration_configure_handler + }; + + zxdg_toplevel_decoration_v1_add_listener(win->src.decoration, &xdg_decoration_listener, win); + + /* we want no decorations */ + if ((flags & RGFW_windowNoBorder)) { + decoration_mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; + } + + zxdg_toplevel_decoration_v1_set_mode(win->src.decoration, decoration_mode); + + /* no xdg_decoration support */ + } else if (!(flags & RGFW_windowNoBorder)) { + /* TODO, some fallback */ + #ifdef RGFW_LIBDECOR + static struct libdecor_interface interface = { + .error = NULL, + }; + + static struct libdecor_frame_interface frameInterface = {0}; /*= { + RGFW_wl_handle_configure, + RGFW_wl_handle_close, + RGFW_wl_handle_commit, + RGFW_wl_handle_dismiss_popup, + };*/ + + win->src.decorContext = libdecor_new(_RGFW->wl_display, &interface); + if (win->src.decorContext) { + struct libdecor_frame *frame = libdecor_decorate(win->src.decorContext, win->src.surface, &frameInterface, win); + if (!frame) { + libdecor_unref(win->src.decorContext); + win->src.decorContext = NULL; + } else { + libdecor_frame_set_app_id(frame, "my-libdecor-app"); + libdecor_frame_set_title(frame, "My Libdecor Window"); + } + } + #endif + } + + if (_RGFW->icon_manager != NULL) { + /* set the default wayland icon */ + xdg_toplevel_icon_manager_v1_set_icon(_RGFW->icon_manager, win->src.xdg_toplevel, NULL); + } + + wl_surface_commit(win->src.surface); + wl_display_dispatch(_RGFW->wl_display); + RGFW_UNUSED(name); + + return win; +} + +RGFW_bool RGFW_FUNC(RGFW_getGlobalMouse) (i32* x, i32* y) { + RGFW_init(); + if (x) *x = 0; + if (y) *y = 0; + return RGFW_FALSE; +} + +u8 RGFW_FUNC(RGFW_rgfwToKeyChar)(u32 key) { + return (u8)key; +} + +void RGFW_FUNC(RGFW_pollEvents) (void) { + RGFW_resetPrevState(); + + /* send buffered requests to compositor */ + while (wl_display_flush(_RGFW->wl_display) == -1) { + /* compositor not responding to new requests */ + /* so let's dispatch some events so the compositor responds */ + if (errno == EAGAIN) { + if (wl_display_dispatch_pending(_RGFW->wl_display) == -1) { + return; + } + } else { + return; + } + } + + /* read the events; if empty this reads from the */ + /* wayland file descriptor */ + if (wl_display_dispatch(_RGFW->wl_display) == -1) { + return; + } + +} + +void RGFW_FUNC(RGFW_window_move) (RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + win->x = x; + win->y = y; +} + + +void RGFW_FUNC(RGFW_window_resize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->w = w; + win->h = h; + if (_RGFW->compositor) { + xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->w, win->h); + #ifdef RGFW_OPENGL + if (win->src.ctx.egl) + wl_egl_window_resize(win->src.ctx.egl->eglWindow, (i32)w, (i32)h, 0, 0); + #endif + } +} + +void RGFW_FUNC(RGFW_window_setAspectRatio) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + if (w == 0 && h == 0) + return; + xdg_toplevel_set_max_size(win->src.xdg_toplevel, (i32)w, (i32)h); +} + +void RGFW_FUNC(RGFW_window_setMinSize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + xdg_toplevel_set_min_size(win->src.xdg_toplevel, w, h); +} + +void RGFW_FUNC(RGFW_window_setMaxSize) (RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + xdg_toplevel_set_max_size(win->src.xdg_toplevel, w, h); +} + +void RGFW_toggleWaylandMaximized(RGFW_window* win, RGFW_bool maximized) { + win->src.maximized = maximized; + if (maximized) { + xdg_toplevel_set_maximized(win->src.xdg_toplevel); + } else { + xdg_toplevel_unset_maximized(win->src.xdg_toplevel); + } +} + +void RGFW_FUNC(RGFW_window_maximize) (RGFW_window* win) { + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + RGFW_toggleWaylandMaximized(win, 1); + return; +} + +void RGFW_FUNC(RGFW_window_focus)(RGFW_window* win) { + RGFW_ASSERT(win); +} + +void RGFW_FUNC(RGFW_window_raise)(RGFW_window* win) { + RGFW_ASSERT(win); +} + +void RGFW_FUNC(RGFW_window_setFullscreen)(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + if (fullscreen) { + + win->internal.flags |= RGFW_windowFullscreen; + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + xdg_toplevel_set_fullscreen(win->src.xdg_toplevel, NULL); /* let the compositor decide */ + } else { + win->internal.flags &= ~(u32)RGFW_windowFullscreen; + xdg_toplevel_unset_fullscreen(win->src.xdg_toplevel); + } + +} + +void RGFW_FUNC(RGFW_window_setFloating) (RGFW_window* win, RGFW_bool floating) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(floating); +} + +void RGFW_FUNC(RGFW_window_setOpacity) (RGFW_window* win, u8 opacity) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(opacity); +} + +void RGFW_FUNC(RGFW_window_minimize)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (RGFW_window_isMaximized(win)) return; + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + win->src.minimized = RGFW_TRUE; + xdg_toplevel_set_minimized(win->src.xdg_toplevel); +} + +void RGFW_FUNC(RGFW_window_restore)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_toggleWaylandMaximized(win, RGFW_FALSE); + + RGFW_window_move(win, win->internal.oldX, win->internal.oldY); + RGFW_window_resize(win, win->internal.oldW, win->internal.oldH); + + RGFW_window_show(win); + RGFW_window_move(win, win->internal.oldX, win->internal.oldY); + RGFW_window_resize(win, win->internal.oldW, win->internal.oldH); + + RGFW_window_show(win); +} + +RGFW_bool RGFW_FUNC(RGFW_window_isFloating)(RGFW_window* win) { + return (!RGFW_window_isFullscreen(win) && !RGFW_window_isMaximized(win)); +} + +void RGFW_FUNC(RGFW_window_setName) (RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + if (name == NULL) name = "\0"; + + if (_RGFW->compositor) + xdg_toplevel_set_title(win->src.xdg_toplevel, name); +} + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_FUNC(RGFW_window_setMousePassthrough) (RGFW_window* win, RGFW_bool passthrough) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(passthrough); +} +#endif /* RGFW_NO_PASSTHROUGH */ + +RGFW_bool RGFW_FUNC(RGFW_window_setIconEx) (RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(type); + + if (_RGFW->icon_manager == NULL || w != h) return RGFW_FALSE; + + if (win->src.icon) { + xdg_toplevel_icon_v1_destroy(win->src.icon); + win->src.icon= NULL; + } + + RGFW_surface* surface = RGFW_createSurface(data, w, h, format); + + if (surface == NULL) return RGFW_FALSE; + + RGFW_copyImageData(surface->native.buffer, RGFW_MIN(w, surface->w), RGFW_MIN(h, surface->h), surface->native.format, surface->data, surface->format); + + win->src.icon = xdg_toplevel_icon_manager_v1_create_icon(_RGFW->icon_manager); + xdg_toplevel_icon_v1_add_buffer(win->src.icon, surface->native.wl_buffer, 1); + xdg_toplevel_icon_manager_v1_set_icon(_RGFW->icon_manager, win->src.xdg_toplevel, win->src.icon); + + RGFW_surface_free(surface); + return RGFW_TRUE; +} + +RGFW_mouse* RGFW_FUNC(RGFW_loadMouse)(u8* data, i32 w, i32 h, RGFW_format format) { + + RGFW_surface *mouse_surface = RGFW_createSurface(data, w, h, format); + + if (mouse_surface == NULL) return NULL; + + RGFW_copyImageData(mouse_surface->native.buffer, RGFW_MIN(w, mouse_surface->w), RGFW_MIN(h, mouse_surface->h), mouse_surface->native.format, mouse_surface->data, mouse_surface->format); + + return (void*) mouse_surface; +} + +void RGFW_FUNC(RGFW_window_setMouse)(RGFW_window* win, RGFW_mouse* mouse) { + RGFW_ASSERT(win); RGFW_ASSERT(mouse); + RGFW_surface *mouse_surface = (RGFW_surface*)mouse; + + win->src.using_custom_cursor = RGFW_TRUE; + + struct wl_buffer *mouse_buffer = mouse_surface->native.wl_buffer; + + wl_surface_attach(win->src.custom_cursor_surface, mouse_buffer, 0, 0); + wl_surface_damage(win->src.custom_cursor_surface, 0, 0, mouse_surface->w, mouse_surface->h); + wl_surface_commit(win->src.custom_cursor_surface); + +} + +void RGFW_FUNC(RGFW_freeMouse)(RGFW_mouse* mouse) { + if (mouse != NULL) { + RGFW_surface_free((RGFW_surface*)mouse); + } +} + +void RGFW_FUNC(RGFW_window_moveMouse)(RGFW_window* win, i32 x, i32 y) { + RGFW_UNUSED(win); RGFW_UNUSED(x); RGFW_UNUSED(y); +} + +RGFW_bool RGFW_FUNC(RGFW_window_setMouseDefault)(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} + +RGFW_bool RGFW_FUNC(RGFW_window_setMouseStandard)(RGFW_window* win, u8 mouse) { + RGFW_ASSERT(win != NULL); + + char* cursorName = NULL; + switch (mouse) { + case RGFW_mouseNormal: cursorName = (char*)"left_ptr"; break; + case RGFW_mouseArrow: cursorName = (char*)"left_ptr"; break; + case RGFW_mouseIbeam: cursorName = (char*)"xterm"; break; + case RGFW_mouseCrosshair: cursorName = (char*)"crosshair"; break; + case RGFW_mousePointingHand: cursorName = (char*)"hand2"; break; + case RGFW_mouseResizeEW: cursorName = (char*)"sb_h_double_arrow"; break; + case RGFW_mouseResizeNS: cursorName = (char*)"sb_v_double_arrow"; break; + case RGFW_mouseResizeNWSE: cursorName = (char*)"top_left_corner"; break; /* or fd_double_arrow */ + case RGFW_mouseResizeNESW: cursorName = (char*)"top_right_corner"; break; /* or bd_double_arrow */ + case RGFW_mouseResizeNW: cursorName = (char*)"top_left_corner"; break; + case RGFW_mouseResizeN: cursorName = (char*)"top_side"; break; + case RGFW_mouseResizeNE: cursorName = (char*)"top_right_corner"; break; + case RGFW_mouseResizeE: cursorName = (char*)"right_side"; break; + case RGFW_mouseResizeSE: cursorName = (char*)"bottom_right_corner"; break; + case RGFW_mouseResizeS: cursorName = (char*)"bottom_side"; break; + case RGFW_mouseResizeSW: cursorName = (char*)"bottom_left_corner"; break; + case RGFW_mouseResizeW: cursorName = (char*)"left_side"; break; + case RGFW_mouseResizeAll: cursorName = (char*)"fleur"; break; + case RGFW_mouseNotAllowed: cursorName = (char*)"not-allowed"; break; + case RGFW_mouseWait: cursorName = (char*)"watch"; break; + case RGFW_mouseProgress: cursorName = (char*)"watch"; break; + default: return RGFW_FALSE; + } + + win->src.using_custom_cursor = RGFW_FALSE; + + struct wl_cursor* wlcursor = wl_cursor_theme_get_cursor(_RGFW->wl_cursor_theme, cursorName); + struct wl_cursor_image* cursor_image = wlcursor->images[0]; + struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(cursor_image); + wl_pointer_set_cursor(_RGFW->wl_pointer, _RGFW->mouse_enter_serial, _RGFW->cursor_surface, (i32)cursor_image->hotspot_x, (i32)cursor_image->hotspot_y); + wl_surface_attach(_RGFW->cursor_surface, cursor_buffer, 0, 0); + wl_surface_damage(_RGFW->cursor_surface, 0, 0, (i32)cursor_image->width, (i32)cursor_image->height); + wl_surface_commit(_RGFW->cursor_surface); + return RGFW_TRUE; +} + +void RGFW_FUNC(RGFW_window_hide) (RGFW_window* win) { + wl_surface_attach(win->src.surface, NULL, 0, 0); + wl_surface_commit(win->src.surface); + win->internal.flags |= RGFW_windowHide; +} + +void RGFW_FUNC(RGFW_window_show) (RGFW_window* win) { + win->internal.flags &= ~(u32)RGFW_windowHide; + if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); + /* wl_surface_attach(win->src.surface, win->x, win->y, win->w, win->h, 0, 0); */ + wl_surface_commit(win->src.surface); +} + +void RGFW_FUNC(RGFW_window_flash) (RGFW_window* win, RGFW_flashRequest request) { + if (RGFW_window_isInFocus(win) && request) { + return; + } +} + +RGFW_ssize_t RGFW_FUNC(RGFW_readClipboardPtr) (char* str, size_t strCapacity) { + + RGFW_UNUSED(strCapacity); + + if (str != NULL) + RGFW_STRNCPY(str, _RGFW->clipboard, _RGFW->clipboard_len - 1); + _RGFW->clipboard[_RGFW->clipboard_len - 1] = '\0'; + return (RGFW_ssize_t)_RGFW->clipboard_len - 1; +} + +void RGFW_FUNC(RGFW_writeClipboard) (const char* text, u32 textLen) { + + // compositor does not support wl_data_device_manager + // clients cannot read rgfw's clipboard + if (_RGFW->data_device_manager == NULL) return; + // clear the clipboard + if (_RGFW->clipboard) + RGFW_FREE(_RGFW->clipboard); + + // set the contents + _RGFW->clipboard = (char*)RGFW_ALLOC(textLen); + RGFW_ASSERT(_RGFW->clipboard != NULL); + RGFW_STRNCPY(_RGFW->clipboard, text, textLen - 1); + _RGFW->clipboard[textLen - 1] = '\0'; + _RGFW->clipboard_len = textLen; + + // means we already wrote to the clipboard + // so destroy it to create a new one + RGFW_window* win = _RGFW->kbOwner; + + if (win->src.data_source != NULL) { + wl_data_source_destroy(win->src.data_source); + win->src.data_source = NULL; + } + + // advertise to other clients that we offer text + win->src.data_source = wl_data_device_manager_create_data_source(_RGFW->data_device_manager); + + // basic error checking + if (win->src.data_source == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, "Could not create clipboard data source"); + return; + } + wl_data_source_offer(win->src.data_source , "text/plain;charset=utf-8"); + + // needed RGFW_doNothing because wayland will call the functions + // if not set they are random data that lead to a crash + static const struct wl_data_source_listener data_source_listener = { + .target = (void (*)(void *, struct wl_data_source *, const char *))&RGFW_doNothing, + .action = (void (*)(void *, struct wl_data_source *, u32))&RGFW_doNothing, + .dnd_drop_performed = (void (*)(void *, struct wl_data_source *))&RGFW_doNothing, + .dnd_finished = (void (*)(void *, struct wl_data_source *))&RGFW_doNothing, + .send = RGFW_wl_data_source_send, + .cancelled = RGFW_wl_data_source_cancelled + }; + + wl_data_source_add_listener(win->src.data_source, &data_source_listener, _RGFW); + +} + +RGFW_bool RGFW_FUNC(RGFW_window_isHidden) (RGFW_window* win) { + RGFW_ASSERT(win != NULL); + return RGFW_FALSE; +} + +RGFW_bool RGFW_FUNC(RGFW_window_isMinimized) (RGFW_window* win) { + RGFW_ASSERT(win != NULL); + return win->src.minimized; +} + +RGFW_bool RGFW_FUNC(RGFW_window_isMaximized) (RGFW_window* win) { + RGFW_ASSERT(win != NULL); + return win->src.maximized; +} + +RGFW_monitor* RGFW_FUNC(RGFW_getMonitors) (size_t* len) { + static RGFW_monitor monitors[RGFW_MAX_MONITORS]; + RGFW_init(); + if (len != NULL) { + *len = _RGFW->monitors.count; + } + + u8 i = 0; + RGFW_monitorNode* cur_node = _RGFW->monitors.list.head; + while (cur_node != NULL) { + monitors[i] = cur_node->mon; + ++i; + cur_node = cur_node->next; + } + return monitors; +} + +RGFW_monitor RGFW_FUNC(RGFW_getPrimaryMonitor) (void) { + return _RGFW->monitors.list.head->mon; +} + +RGFW_bool RGFW_FUNC(RGFW_monitor_requestMode) (RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { + RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); + return RGFW_FALSE; +} + +RGFW_monitor RGFW_FUNC(RGFW_window_getMonitor) (RGFW_window* win) { + RGFW_ASSERT(win); + return win->src.active_monitor; +} + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_FUNC(RGFW_extensionSupportedPlatform_OpenGL) (const char * extension, size_t len) { return RGFW_extensionSupportedPlatform_EGL(extension, len); } +RGFW_proc RGFW_FUNC(RGFW_getProcAddress_OpenGL) (const char* procname) { return RGFW_getProcAddress_EGL(procname); } + + +RGFW_bool RGFW_FUNC(RGFW_window_createContextPtr_OpenGL)(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { + RGFW_bool out = RGFW_window_createContextPtr_EGL(win, &ctx->egl, hints); + win->src.gfxType = RGFW_gfxNativeOpenGL; + + RGFW_window_swapInterval_OpenGL(win, 0); + return out; +} +void RGFW_FUNC(RGFW_window_deleteContextPtr_OpenGL) (RGFW_window* win, RGFW_glContext* ctx) { RGFW_window_deleteContextPtr_EGL(win, &ctx->egl); win->src.ctx.native = NULL; } + +void RGFW_FUNC(RGFW_window_makeCurrentContext_OpenGL) (RGFW_window* win) { RGFW_window_makeCurrentContext_EGL(win); } +void* RGFW_FUNC(RGFW_getCurrentContext_OpenGL) (void) { return RGFW_getCurrentContext_EGL(); } +void RGFW_FUNC(RGFW_window_swapBuffers_OpenGL) (RGFW_window* win) { RGFW_window_swapBuffers_EGL(win); } +void RGFW_FUNC(RGFW_window_swapInterval_OpenGL) (RGFW_window* win, i32 swapInterval) { RGFW_window_swapInterval_EGL(win, swapInterval); } +#endif /* RGFW_OPENGL */ + +void RGFW_FUNC(RGFW_window_closePlatform)(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, "a window was freed"); + #ifdef RGFW_LIBDECOR + if (win->src.decorContext) + libdecor_unref(win->src.decorContext); + #endif + + if (win->src.decoration) { + zxdg_toplevel_decoration_v1_destroy(win->src.decoration); + } + + if (win->src.xdg_toplevel) { + xdg_toplevel_destroy(win->src.xdg_toplevel); + } + + wl_surface_destroy(win->src.custom_cursor_surface); + + if (win->src.locked_pointer) { + zwp_locked_pointer_v1_destroy(win->src.locked_pointer); + } + + if (win->src.icon) { + xdg_toplevel_icon_v1_destroy(win->src.icon); + } + + xdg_surface_destroy(win->src.xdg_surface); + wl_surface_destroy(win->src.surface); +} + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_FUNC(RGFW_window_createSurface_WebGPU) (RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + WGPUSurfaceSourceWaylandSurface fromWl = {0}; + fromWl.chain.sType = WGPUSType_SurfaceSourceWaylandSurface; + fromWl.display = _RGFW->wl_display; + fromWl.surface = window->src.surface; + + surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromWl.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#endif + + + +#endif /* RGFW_WAYLAND */ +/* + End of Wayland defines +*/ + +/* + + Start of Windows defines + + +*/ + +#ifdef RGFW_WINDOWS +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +#ifndef OEMRESOURCE + #define OEMRESOURCE +#endif + +#include + +#ifndef OCR_NORMAL +#define OCR_NORMAL 32512 +#define OCR_IBEAM 32513 +#define OCR_WAIT 32514 +#define OCR_CROSS 32515 +#define OCR_UP 32516 +#define OCR_SIZENWSE 32642 +#define OCR_SIZENESW 32643 +#define OCR_SIZEWE 32644 +#define OCR_SIZENS 32645 +#define OCR_SIZEALL 32646 +#define OCR_NO 32648 +#define OCR_HAND 32649 +#define OCR_APPSTARTING 32650 +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif + +RGFW_bool RGFW_createUTF8FromWideStringWin32(const WCHAR* source, char* out, size_t max); + +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 + +typedef int (*PFN_wglGetSwapIntervalEXT)(void); +PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL; +#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc + +/* these two wgl functions need to be preloaded */ +typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList); +PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; + +HMODULE RGFW_wgl_dll = NULL; + +#ifndef RGFW_NO_LOAD_WGL + typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC); + typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC); + typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR); + typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC); + typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void); + typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void); + typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC); + + PFN_wglCreateContext wglCreateContextSRC; + PFN_wglDeleteContext wglDeleteContextSRC; + PFN_wglGetProcAddress wglGetProcAddressSRC; + PFN_wglMakeCurrent wglMakeCurrentSRC; + PFN_wglGetCurrentDC wglGetCurrentDCSRC; + PFN_wglGetCurrentContext wglGetCurrentContextSRC; + PFN_wglShareLists wglShareListsSRC; + + #define wglCreateContext wglCreateContextSRC + #define wglDeleteContext wglDeleteContextSRC + #define wglGetProcAddress wglGetProcAddressSRC + #define wglMakeCurrent wglMakeCurrentSRC + #define wglGetCurrentDC wglGetCurrentDCSRC + #define wglGetCurrentContext wglGetCurrentContextSRC + #define wglShareLists wglShareListsSRC +#endif + +void* RGFW_window_getHWND(RGFW_window* win) { return win->src.window; } +void* RGFW_window_getHDC(RGFW_window* win) { return win->src.hdc; } + +#ifdef RGFW_OPENGL +RGFWDEF void RGFW_win32_loadOpenGLFuncs(HWND dummyWin); + +typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats); +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL; + +typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval); +PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; +#endif + +#ifndef RGFW_NO_DWM +HMODULE RGFW_dwm_dll = NULL; +#ifndef _DWMAPI_H_ +typedef struct { DWORD dwFlags; int fEnable; HRGN hRgnBlur; int fTransitionOnMaximized;} DWM_BLURBEHIND; +#endif +typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND, const DWM_BLURBEHIND*); +PFN_DwmEnableBlurBehindWindow DwmEnableBlurBehindWindowSRC = NULL; + +typedef HRESULT (WINAPI * PFN_DwmSetWindowAttribute)(HWND, DWORD, LPCVOID, DWORD); +PFN_DwmSetWindowAttribute DwmSetWindowAttributeSRC = NULL; +#endif +void RGFW_win32_makeWindowTransparent(RGFW_window* win); +void RGFW_win32_makeWindowTransparent(RGFW_window* win) { + if (!(win->internal.flags & RGFW_windowTransparent)) return; + + #ifndef RGFW_NO_DWM + if (DwmEnableBlurBehindWindowSRC != NULL) { + DWM_BLURBEHIND bb = {0, 0, 0, 0}; + bb.dwFlags = 0x1; + bb.fEnable = TRUE; + bb.hRgnBlur = NULL; + DwmEnableBlurBehindWindowSRC(win->src.window, &bb); + + } else + #endif + { + SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED); + SetLayeredWindowAttributes(win->src.window, 0, 128, LWA_ALPHA); + } +} + +RGFWDEF RGFW_bool RGFW_win32_getDarkModeState(void); +RGFW_bool RGFW_win32_getDarkModeState(void) { + u32 lightMode = 1; + DWORD len = sizeof(lightMode); + + RegGetValueW( + HKEY_CURRENT_USER, + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + L"AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, &lightMode, &len + ); + + return (lightMode == 0); +} + +RGFWDEF void RGFW_win32_makeWindowDarkMode(RGFW_window* win, RGFW_bool state); +void RGFW_win32_makeWindowDarkMode(RGFW_window* win, RGFW_bool state) { + BOOL value = (state == RGFW_TRUE) ? TRUE : FALSE; + DwmSetWindowAttributeSRC(win->src.window, 20 /* DWMWA_USE_IMMERSIVE_DARK_MODE */, &value, sizeof(value)); +} + +LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + RGFW_window* win = (RGFW_window*)GetPropW(hWnd, L"RGFW"); + if (win == NULL) return DefWindowProcW(hWnd, message, wParam, lParam); + + static BYTE keyboardState[256]; + GetKeyboardState(keyboardState); + + RECT windowRect; + GetWindowRect(hWnd, &windowRect); + + switch (message) { + case WM_CLOSE: + case WM_QUIT: + RGFW_windowQuitCallback(win); + return 0; + case WM_ACTIVATE: { + RGFW_bool inFocus = RGFW_BOOL(LOWORD(wParam) != WA_INACTIVE); + RGFW_focusCallback(win, inFocus); + + return DefWindowProcW(hWnd, message, wParam, lParam); + } + case WM_MOVE: + if (win->internal.captureMouse) { + RGFW_window_captureMousePlatform(win, RGFW_TRUE); + } + + RGFW_windowMovedCallback(win, windowRect.left, windowRect.top); + return DefWindowProcW(hWnd, message, wParam, lParam); + case WM_SIZE: { + if (win->internal.captureMouse) { + RGFW_window_captureMousePlatform(win, RGFW_TRUE); + } + + if (win->src.aspectRatioW != 0 && win->src.aspectRatioH != 0) { + double aspectRatio = (double)win->src.aspectRatioW / win->src.aspectRatioH; + + int width = windowRect.right - windowRect.left; + int height = windowRect.bottom - windowRect.top; + int newHeight = (int)(width / aspectRatio); + int newWidth = (int)(height * aspectRatio); + + if (win->w > (i32)((windowRect.right - windowRect.left) - win->src.offsetW) || + win->h > (i32)((windowRect.bottom - windowRect.top) - win->src.offsetH)) + { + if (newHeight > height) windowRect.right = windowRect.left + newWidth; + else windowRect.bottom = windowRect.top + newHeight; + } else { + if (newHeight < height) windowRect.right = windowRect.left + newWidth; + else windowRect.bottom = windowRect.top + newHeight; + } + + RGFW_window_resize(win, (windowRect.right - windowRect.left) - win->src.offsetW, + (windowRect.bottom - windowRect.top) - win->src.offsetH); + } + + i32 w = (windowRect.right - windowRect.left) - (i32)win->src.offsetW; + i32 h = (windowRect.bottom - windowRect.top) - (i32)win->src.offsetH; + RGFW_windowResizedCallback(win, w, h); + RGFW_window_checkMode(win); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + case WM_MOUSEACTIVATE: { + if (HIWORD(lParam) == WM_LBUTTONDOWN) { + if (LOWORD(lParam) != HTCLIENT) + win->src.actionFrame = RGFW_TRUE; + } + + break; + } + case WM_CAPTURECHANGED: { + if (lParam == 0 && win->src.actionFrame) { + RGFW_window_captureMousePlatform(win, win->internal.captureMouse); + win->src.actionFrame = RGFW_FALSE; + } + + break; + } + #ifndef RGFW_NO_MONITOR + case WM_DPICHANGED: { + const float scaleX = HIWORD(wParam) / (float) 96; + const float scaleY = LOWORD(wParam) / (float) 96; + + RGFW_scaleUpdatedCallback(win, scaleX, scaleY); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + #endif + case WM_GETMINMAXINFO: { + MINMAXINFO* mmi = (MINMAXINFO*) lParam; + mmi->ptMinTrackSize.x = (LONG)(win->src.minSizeW + win->src.offsetW); + mmi->ptMinTrackSize.y = (LONG)(win->src.minSizeH + win->src.offsetH); + if (win->src.maxSizeW == 0 && win->src.maxSizeH == 0) + return DefWindowProcW(hWnd, message, wParam, lParam); + + mmi->ptMaxTrackSize.x = (LONG)(win->src.maxSizeW + win->src.offsetW); + mmi->ptMaxTrackSize.y = (LONG)(win->src.maxSizeH + win->src.offsetH); + return DefWindowProcW(hWnd, message, wParam, lParam); + } + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hWnd, &ps); + RGFW_windowRefreshCallback(win); + EndPaint(hWnd, &ps); + + return DefWindowProcW(hWnd, message, wParam, lParam); + } + #if(_WIN32_WINNT >= 0x0600) + case WM_DWMCOMPOSITIONCHANGED: + case WM_DWMCOLORIZATIONCOLORCHANGED: + RGFW_win32_makeWindowTransparent(win); + break; + #endif + + case WM_ENTERSIZEMOVE: { + if (win->src.actionFrame) + RGFW_window_captureMousePlatform(win, win->internal.captureMouse); + + #ifdef RGFW_ADVANCED_SMOOTH_RESIZE + SetTimer(win->src.window, 1, USER_TIMER_MINIMUM, NULL); break; + #endif + break; + } + case WM_EXITSIZEMOVE: { + if (win->src.actionFrame) + RGFW_window_captureMousePlatform(win, win->internal.captureMouse); + + #ifdef RGFW_ADVANCED_SMOOTH_RESIZE + KillTimer(win->src.window, 1); break; + #endif + break; + } + case WM_TIMER: + RGFW_windowRefreshCallback(win); + break; + + case WM_NCLBUTTONDOWN: { + /* workaround for half-second pause when starting to move window + see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/ + */ + POINT point = { 0, 0 }; + if (SendMessage(win->src.window, WM_NCHITTEST, wParam, lParam) != HTCAPTION || GetCursorPos(&point) == FALSE) + break; + + ScreenToClient(win->src.window, &point); + PostMessage(win->src.window, WM_MOUSEMOVE, 0, (u32)(point.x)|((u32)(point.y) << 16)); + break; + } + case WM_MOUSELEAVE: + RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE); + break; + case WM_SYSKEYUP: case WM_KEYUP: { + if (!(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return DefWindowProcW(hWnd, message, wParam, lParam); + i32 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff)); + if (scancode == 0) + scancode = (i32)MapVirtualKeyW((UINT)wParam, MAPVK_VK_TO_VSC); + + switch (scancode) { + case 0x54: scancode = 0x137; break; /* Alt+PrtS */ + case 0x146: scancode = 0x45; break; /* Ctrl+Pause */ + case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */ + default: break; + } + + RGFW_key value = (u8)RGFW_apiKeyToRGFW((u32) scancode); + + if (wParam == VK_CONTROL) { + if (HIWORD(lParam) & KF_EXTENDED) + value = RGFW_controlR; + else value = RGFW_controlL; + } + + wchar_t charBuffer; + ToUnicodeEx((UINT)wParam, (UINT)scancode, keyboardState, (wchar_t*)&charBuffer, 1, 0, NULL); + + u8 sym = (u8)charBuffer; + + RGFW_bool repeat = ((lParam & 0x40000000) != 0) || RGFW_window_isKeyDown(win, value); + + RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001)); + RGFW_keyCallback(win, value, sym, win->internal.mod, repeat, RGFW_FALSE); + break; + } + case WM_SYSKEYDOWN: case WM_KEYDOWN: { + if (!(win->internal.enabledEvents & RGFW_keyPressedFlag)) return DefWindowProcW(hWnd, message, wParam, lParam); + i32 scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff)); + if (scancode == 0) + scancode = (i32)MapVirtualKeyW((u32)wParam, MAPVK_VK_TO_VSC); + + switch (scancode) { + case 0x54: scancode = 0x137; break; /* Alt+PrtS */ + case 0x146: scancode = 0x45; break; /* Ctrl+Pause */ + case 0x136: scancode = 0x36; break; /* CJK IME sets the extended bit for right Shift */ + default: break; + } + + RGFW_key value = (u8)RGFW_apiKeyToRGFW((u32) scancode); + if (wParam == VK_CONTROL) { + if (HIWORD(lParam) & KF_EXTENDED) + value = RGFW_controlR; + else value = RGFW_controlL; + } + + wchar_t charBuffer; + ToUnicodeEx((UINT)wParam, (UINT)scancode, keyboardState, &charBuffer, 1, 0, NULL); + u8 sym = (u8)charBuffer; + + RGFW_bool repeat = ((lParam & 0x40000000) != 0) || RGFW_window_isKeyDown(win, value); + + RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001)); + RGFW_keyCallback(win, value, sym, win->internal.mod, repeat, 1); + break; + } + case WM_MOUSEMOVE: { + if (win->internal.mouseInside == RGFW_FALSE) { + RGFW_mouseNotifyCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), RGFW_TRUE); + } + + if ((win->internal.rawMouse) || _RGFW->rawMouse) { + return DefWindowProcW(hWnd, message, wParam, lParam); + } + + RGFW_mousePosCallback(win, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), _RGFW->vectorX, _RGFW->vectorY); + break; + } + case WM_INPUT: { + if (!(win->internal.rawMouse || _RGFW->rawMouse)) return DefWindowProcW(hWnd, message, wParam, lParam); + unsigned size = sizeof(RAWINPUT); + static RAWINPUT raw; + + GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER)); + + if (raw.header.dwType != RIM_TYPEMOUSE || (raw.data.mouse.lLastX == 0 && raw.data.mouse.lLastY == 0) ) + break; + + float vecX = 0.0f; + float vecY = 0.0f; + + if (raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { + POINT pos = {0, 0}; + int width, height; + + if (raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) { + pos.x += GetSystemMetrics(SM_XVIRTUALSCREEN); + pos.y += GetSystemMetrics(SM_YVIRTUALSCREEN); + width = GetSystemMetrics(SM_CXVIRTUALSCREEN); + height = GetSystemMetrics(SM_CYVIRTUALSCREEN); + } + else { + width = GetSystemMetrics(SM_CXSCREEN); + height = GetSystemMetrics(SM_CYSCREEN); + } + + pos.x += (int) (((float)raw.data.mouse.lLastX / 65535.f) * (float)width); + pos.y += (int) (((float)raw.data.mouse.lLastY / 65535.f) * (float)height); + ScreenToClient(win->src.window, &pos); + + vecX = (float)(pos.x - win->internal.lastMouseX); + vecY = (float)(pos.y - win->internal.lastMouseY); + } else { + vecX = (float)(raw.data.mouse.lLastX); + vecY = (float)(raw.data.mouse.lLastY); + } + + RGFW_mousePosCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, vecX, vecY); + break; + } + case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: { + RGFW_mouseButton value = 0; + if (message == WM_XBUTTONDOWN) + value = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(wParam) == XBUTTON2); + else value = (message == WM_LBUTTONDOWN) ? (u8)RGFW_mouseLeft : + (message == WM_RBUTTONDOWN) ? (u8)RGFW_mouseRight : (u8)RGFW_mouseMiddle; + + RGFW_mouseButtonCallback(win, value, 1); + break; + } + case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP: { + RGFW_mouseButton value = 0; + if (message == WM_XBUTTONUP) + value = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(wParam) == XBUTTON2); + else value = (message == WM_LBUTTONUP) ? (u8)RGFW_mouseLeft : + (message == WM_RBUTTONUP) ? (u8)RGFW_mouseRight : (u8)RGFW_mouseMiddle; + + RGFW_mouseButtonCallback(win, value, 0); + break; + } + case WM_MOUSEWHEEL: { + float scrollY = (float)((i16) HIWORD(wParam) / (double) WHEEL_DELTA); + RGFW_mouseScrollCallback(win, 0.0f, scrollY); + break; + } + case 0x020E: {/* WM_MOUSEHWHEEL */ + float scrollX = -(float)((i16) HIWORD(wParam) / (double) WHEEL_DELTA); + RGFW_mouseScrollCallback(win, scrollX, 0.0f); + break; + } + case WM_DROPFILES: { + HDROP drop = (HDROP) wParam; + POINT pt; + + /* Move the mouse to the position of the drop */ + DragQueryPoint(drop, &pt); + RGFW_dataDragCallback(win, pt.x, pt.y); + + if (!(win->internal.enabledEvents & RGFW_dataDrop)) return DefWindowProcW(hWnd, message, wParam, lParam); + char** files = _RGFW->files; + size_t count = DragQueryFileW(drop, 0xffffffff, NULL, 0); + + u32 i; + for (i = 0; i < count; i++) { + UINT length = DragQueryFileW(drop, i, NULL, 0); + if (length == 0) + continue; + + WCHAR buffer[RGFW_MAX_PATH * 2]; + if (length > (RGFW_MAX_PATH * 2) - 1) + length = RGFW_MAX_PATH * 2; + + DragQueryFileW(drop, i, buffer, length + 1); + + RGFW_createUTF8FromWideStringWin32(buffer, files[i], RGFW_MAX_PATH); + + files[i][RGFW_MAX_PATH - 1] = '\0'; + } + + DragFinish(drop); + + RGFW_dataDropCallback(win, files, count); + break; + } + default: break; + } + + return DefWindowProcW(hWnd, message, wParam, lParam); +} + +#ifndef RGFW_NO_DPI + HMODULE RGFW_Shcore_dll = NULL; + typedef HRESULT (WINAPI *PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); + PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL; + #define GetDpiForMonitor GetDpiForMonitorSRC +#endif + +#if !defined(RGFW_NO_LOAD_WINMM) && !defined(RGFW_NO_WINMM) + HMODULE RGFW_winmm_dll = NULL; + typedef u32 (WINAPI * PFN_timeBeginPeriod)(u32); + typedef PFN_timeBeginPeriod PFN_timeEndPeriod; + PFN_timeBeginPeriod timeBeginPeriodSRC, timeEndPeriodSRC; + #define timeBeginPeriod timeBeginPeriodSRC + #define timeEndPeriod timeEndPeriodSRC +#elif !defined(RGFW_NO_WINMM) + __declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod); + __declspec(dllimport) u32 __stdcall timeEndPeriod(u32 uPeriod); +#endif +#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \ + name##SRC = (PFN_##name)(RGFW_proc)GetProcAddress((proc), (#name)); \ + RGFW_ASSERT(name##SRC != NULL); \ + } + +RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + + BITMAPV5HEADER bi; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = (i32)w; + bi.bV5Height = -((LONG) h); + bi.bV5Planes = 1; + bi.bV5BitCount = (format >= RGFW_formatRGBA8) ? 32 : 24; + bi.bV5Compression = BI_RGB; + + surface->native.bitmap = CreateDIBSection(_RGFW->root->src.hdc, + (BITMAPINFO*) &bi, DIB_RGB_COLORS, + (void**) &surface->native.bitmapBits, + NULL, (DWORD) 0); + + surface->native.format = (format >= RGFW_formatRGBA8) ? RGFW_formatBGRA8 : RGFW_formatBGR8; + + if (surface->native.bitmap == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, "Failed to create DIB section."); + return RGFW_FALSE; + } + + surface->native.hdcMem = CreateCompatibleDC(_RGFW->root->src.hdc); + SelectObject(surface->native.hdcMem, surface->native.bitmap); + + return RGFW_TRUE; +} + +void RGFW_surface_freePtr(RGFW_surface* surface) { + RGFW_ASSERT(surface != NULL); + + DeleteDC(surface->native.hdcMem); + DeleteObject(surface->native.bitmap); +} + +void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { + RGFW_copyImageData(surface->native.bitmapBits, surface->w, RGFW_MIN(win->h, surface->h), surface->native.format, surface->data, surface->format); + BitBlt(win->src.hdc, 0, 0, RGFW_MIN(win->w, surface->w), RGFW_MIN(win->h, surface->h), surface->native.hdcMem, 0, 0, SRCCOPY); +} + +void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); + RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window }; + id.dwFlags = (state == RGFW_TRUE) ? 0 : RIDEV_REMOVE; + + RegisterRawInputDevices(&id, 1, sizeof(id)); +} + +void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { + if (state == RGFW_FALSE) { + ClipCursor(NULL); + return; + } + + RECT clipRect; + GetClientRect(win->src.window, &clipRect); + ClientToScreen(win->src.window, (POINT*) &clipRect.left); + ClientToScreen(win->src.window, (POINT*) &clipRect.right); + ClipCursor(&clipRect); +} + +#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) { x = LoadLibraryA(lib); RGFW_ASSERT(x != NULL); } + +#ifdef RGFW_DIRECTX +int RGFW_window_createSwapChain_DirectX(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain) { + RGFW_ASSERT(win && pFactory && pDevice && swapchain); + + static DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 }; + swapChainDesc.BufferCount = 2; + swapChainDesc.BufferDesc.Width = win->w; + swapChainDesc.BufferDesc.Height = win->h; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.OutputWindow = (HWND)win->src.window; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.Windowed = TRUE; + swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; + + HRESULT hr = pFactory->lpVtbl->CreateSwapChain(pFactory, (IUnknown*)pDevice, &swapChainDesc, swapchain); + if (FAILED(hr)) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errDirectXContext, "Failed to create DirectX swap chain!"); + return -2; + } + + return 0; +} +#endif + +/* we're doing it with magic numbers because some keys are missing */ +void RGFW_initKeycodesPlatform(void) { + _RGFW->keycodes[0x00B] = RGFW_0; + _RGFW->keycodes[0x002] = RGFW_1; + _RGFW->keycodes[0x003] = RGFW_2; + _RGFW->keycodes[0x004] = RGFW_3; + _RGFW->keycodes[0x005] = RGFW_4; + _RGFW->keycodes[0x006] = RGFW_5; + _RGFW->keycodes[0x007] = RGFW_6; + _RGFW->keycodes[0x008] = RGFW_7; + _RGFW->keycodes[0x009] = RGFW_8; + _RGFW->keycodes[0x00A] = RGFW_9; + _RGFW->keycodes[0x01E] = RGFW_a; + _RGFW->keycodes[0x030] = RGFW_b; + _RGFW->keycodes[0x02E] = RGFW_c; + _RGFW->keycodes[0x020] = RGFW_d; + _RGFW->keycodes[0x012] = RGFW_e; + _RGFW->keycodes[0x021] = RGFW_f; + _RGFW->keycodes[0x022] = RGFW_g; + _RGFW->keycodes[0x023] = RGFW_h; + _RGFW->keycodes[0x017] = RGFW_i; + _RGFW->keycodes[0x024] = RGFW_j; + _RGFW->keycodes[0x025] = RGFW_k; + _RGFW->keycodes[0x026] = RGFW_l; + _RGFW->keycodes[0x032] = RGFW_m; + _RGFW->keycodes[0x031] = RGFW_n; + _RGFW->keycodes[0x018] = RGFW_o; + _RGFW->keycodes[0x019] = RGFW_p; + _RGFW->keycodes[0x010] = RGFW_q; + _RGFW->keycodes[0x013] = RGFW_r; + _RGFW->keycodes[0x01F] = RGFW_s; + _RGFW->keycodes[0x014] = RGFW_t; + _RGFW->keycodes[0x016] = RGFW_u; + _RGFW->keycodes[0x02F] = RGFW_v; + _RGFW->keycodes[0x011] = RGFW_w; + _RGFW->keycodes[0x02D] = RGFW_x; + _RGFW->keycodes[0x015] = RGFW_y; + _RGFW->keycodes[0x02C] = RGFW_z; + _RGFW->keycodes[0x028] = RGFW_apostrophe; + _RGFW->keycodes[0x02B] = RGFW_backSlash; + _RGFW->keycodes[0x033] = RGFW_comma; + _RGFW->keycodes[0x00D] = RGFW_equals; + _RGFW->keycodes[0x029] = RGFW_backtick; + _RGFW->keycodes[0x01A] = RGFW_bracket; + _RGFW->keycodes[0x00C] = RGFW_minus; + _RGFW->keycodes[0x034] = RGFW_period; + _RGFW->keycodes[0x01B] = RGFW_closeBracket; + _RGFW->keycodes[0x027] = RGFW_semicolon; + _RGFW->keycodes[0x035] = RGFW_slash; + _RGFW->keycodes[0x056] = RGFW_world2; + _RGFW->keycodes[0x00E] = RGFW_backSpace; + _RGFW->keycodes[0x153] = RGFW_delete; + _RGFW->keycodes[0x14F] = RGFW_end; + _RGFW->keycodes[0x01C] = RGFW_enter; + _RGFW->keycodes[0x001] = RGFW_escape; + _RGFW->keycodes[0x147] = RGFW_home; + _RGFW->keycodes[0x152] = RGFW_insert; + _RGFW->keycodes[0x15D] = RGFW_menu; + _RGFW->keycodes[0x151] = RGFW_pageDown; + _RGFW->keycodes[0x149] = RGFW_pageUp; + _RGFW->keycodes[0x045] = RGFW_pause; + _RGFW->keycodes[0x039] = RGFW_space; + _RGFW->keycodes[0x00F] = RGFW_tab; + _RGFW->keycodes[0x03A] = RGFW_capsLock; + _RGFW->keycodes[0x145] = RGFW_numLock; + _RGFW->keycodes[0x046] = RGFW_scrollLock; + _RGFW->keycodes[0x03B] = RGFW_F1; + _RGFW->keycodes[0x03C] = RGFW_F2; + _RGFW->keycodes[0x03D] = RGFW_F3; + _RGFW->keycodes[0x03E] = RGFW_F4; + _RGFW->keycodes[0x03F] = RGFW_F5; + _RGFW->keycodes[0x040] = RGFW_F6; + _RGFW->keycodes[0x041] = RGFW_F7; + _RGFW->keycodes[0x042] = RGFW_F8; + _RGFW->keycodes[0x043] = RGFW_F9; + _RGFW->keycodes[0x044] = RGFW_F10; + _RGFW->keycodes[0x057] = RGFW_F11; + _RGFW->keycodes[0x058] = RGFW_F12; + _RGFW->keycodes[0x064] = RGFW_F13; + _RGFW->keycodes[0x065] = RGFW_F14; + _RGFW->keycodes[0x066] = RGFW_F15; + _RGFW->keycodes[0x067] = RGFW_F16; + _RGFW->keycodes[0x068] = RGFW_F17; + _RGFW->keycodes[0x069] = RGFW_F18; + _RGFW->keycodes[0x06A] = RGFW_F19; + _RGFW->keycodes[0x06B] = RGFW_F20; + _RGFW->keycodes[0x06C] = RGFW_F21; + _RGFW->keycodes[0x06D] = RGFW_F22; + _RGFW->keycodes[0x06E] = RGFW_F23; + _RGFW->keycodes[0x076] = RGFW_F24; + _RGFW->keycodes[0x038] = RGFW_altL; + _RGFW->keycodes[0x01D] = RGFW_controlL; + _RGFW->keycodes[0x02A] = RGFW_shiftL; + _RGFW->keycodes[0x15B] = RGFW_superL; + _RGFW->keycodes[0x137] = RGFW_printScreen; + _RGFW->keycodes[0x138] = RGFW_altR; + _RGFW->keycodes[0x11D] = RGFW_controlR; + _RGFW->keycodes[0x036] = RGFW_shiftR; + _RGFW->keycodes[0x15C] = RGFW_superR; + _RGFW->keycodes[0x150] = RGFW_down; + _RGFW->keycodes[0x14B] = RGFW_left; + _RGFW->keycodes[0x14D] = RGFW_right; + _RGFW->keycodes[0x148] = RGFW_up; + _RGFW->keycodes[0x052] = RGFW_kp0; + _RGFW->keycodes[0x04F] = RGFW_kp1; + _RGFW->keycodes[0x050] = RGFW_kp2; + _RGFW->keycodes[0x051] = RGFW_kp3; + _RGFW->keycodes[0x04B] = RGFW_kp4; + _RGFW->keycodes[0x04C] = RGFW_kp5; + _RGFW->keycodes[0x04D] = RGFW_kp6; + _RGFW->keycodes[0x047] = RGFW_kp7; + _RGFW->keycodes[0x048] = RGFW_kp8; + _RGFW->keycodes[0x049] = RGFW_kp9; + _RGFW->keycodes[0x04E] = RGFW_kpPlus; + _RGFW->keycodes[0x053] = RGFW_kpPeriod; + _RGFW->keycodes[0x135] = RGFW_kpSlash; + _RGFW->keycodes[0x11C] = RGFW_kpReturn; + _RGFW->keycodes[0x059] = RGFW_kpEqual; + _RGFW->keycodes[0x037] = RGFW_kpMultiply; + _RGFW->keycodes[0x04A] = RGFW_kpMinus; +} + + +i32 RGFW_initPlatform(void) { +#ifndef RGFW_NO_DPI + #if (_WIN32_WINNT >= 0x0600) + SetProcessDPIAware(); + #endif +#endif + + #ifndef RGFW_NO_WINMM + #ifndef RGFW_NO_LOAD_WINMM + RGFW_LOAD_LIBRARY(RGFW_winmm_dll, "winmm.dll"); + RGFW_PROC_DEF(RGFW_winmm_dll, timeBeginPeriod); + RGFW_PROC_DEF(RGFW_winmm_dll, timeEndPeriod); + #endif + timeBeginPeriod(1); + #endif + + #ifndef RGFW_NO_DWM + RGFW_LOAD_LIBRARY(RGFW_dwm_dll, "dwmapi.dll"); + RGFW_PROC_DEF(RGFW_dwm_dll, DwmEnableBlurBehindWindow); + RGFW_PROC_DEF(RGFW_dwm_dll, DwmSetWindowAttribute); + #endif + + RGFW_LOAD_LIBRARY(RGFW_wgl_dll, "opengl32.dll"); + #ifndef RGFW_NO_LOAD_WGL + RGFW_PROC_DEF(RGFW_wgl_dll, wglCreateContext); + RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext); + RGFW_PROC_DEF(RGFW_wgl_dll, wglGetProcAddress); + RGFW_PROC_DEF(RGFW_wgl_dll, wglMakeCurrent); + RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentDC); + RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentContext); + RGFW_PROC_DEF(RGFW_wgl_dll, wglShareLists); + #endif + + u8 RGFW_blk[] = { 0, 0, 0, 0 }; + _RGFW->hiddenMouse = RGFW_loadMouse(RGFW_blk, 1, 1, RGFW_formatRGBA8); + return 1; +} + +RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { + if (name[0] == 0) name = (char*) " "; + win->src.hIconSmall = win->src.hIconBig = NULL; + win->src.maxSizeW = 0; + win->src.maxSizeH = 0; + win->src.minSizeW = 0; + win->src.minSizeH = 0; + win->src.aspectRatioW = 0; + win->src.aspectRatioH = 0; + + HINSTANCE inh = GetModuleHandleA(NULL); + + #ifndef __cplusplus + WNDCLASSW Class = {0}; /*!< Setup the Window class. */ + #else + WNDCLASSW Class = {}; + #endif + + if (_RGFW->className == NULL) + _RGFW->className = (char*)name; + + wchar_t wide_class[256]; + MultiByteToWideChar(CP_UTF8, 0, _RGFW->className, -1, wide_class, 255); + + Class.lpszClassName = wide_class; + Class.hInstance = inh; + Class.hCursor = LoadCursor(NULL, IDC_ARROW); + Class.lpfnWndProc = WndProcW; + Class.cbClsExtra = sizeof(RGFW_window*); + + Class.hIcon = (HICON)LoadImageA(GetModuleHandleW(NULL), "RGFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + if (Class.hIcon == NULL) + Class.hIcon = (HICON)LoadImageA(NULL, (LPCSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + + RegisterClassW(&Class); + + DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + RECT windowRect, clientRect; + + if (!(flags & RGFW_windowNoBorder)) { + window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX; + + if (!(flags & RGFW_windowNoResize)) + window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX; + } else + window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU; + + wchar_t wide_name[256]; + MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 255); + HWND dummyWin = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->x, win->y, win->w, win->h, 0, 0, inh, 0); + + GetWindowRect(dummyWin, &windowRect); + GetClientRect(dummyWin, &clientRect); + +#ifdef RGFW_OPENGL + RGFW_win32_loadOpenGLFuncs(dummyWin); +#endif + + DestroyWindow(dummyWin); + + win->src.offsetW = (i32)(windowRect.right - windowRect.left) - (i32)(clientRect.right - clientRect.left); + win->src.offsetH = (i32)(windowRect.bottom - windowRect.top) - (i32)(clientRect.bottom - clientRect.top); + win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->x, win->y, win->w + (i32)win->src.offsetW, win->h + (i32)win->src.offsetH, 0, 0, inh, 0); + SetPropW(win->src.window, L"RGFW", win); + RGFW_window_resize(win, win->w, win->h); /* so WM_GETMINMAXINFO gets called again */ + + if (flags & RGFW_windowAllowDND) { + win->internal.flags |= RGFW_windowAllowDND; + RGFW_window_setDND(win, 1); + } + win->src.hdc = GetDC(win->src.window); + + RGFW_win32_makeWindowDarkMode(win, RGFW_win32_getDarkModeState()); + RGFW_win32_makeWindowTransparent(win); + return win; +} + +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { + RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border); + LONG style = GetWindowLong(win->src.window, GWL_STYLE); + + if (border == 0) { + SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); + SetWindowPos( + win->src.window, HWND_TOP, 0, 0, 0, 0, + SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE + ); + } + else { + if (win->internal.flags & RGFW_windowNoResize) style &= ~WS_MAXIMIZEBOX; + SetWindowLong(win->src.window, GWL_STYLE, style | WS_OVERLAPPEDWINDOW); + SetWindowPos( + win->src.window, HWND_TOP, 0, 0, 0, 0, + SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE + ); + } +} + +void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) { + RGFW_setBit(&win->internal.flags, RGFW_windowAllowDND, allow); + DragAcceptFiles(win->src.window, allow); +} + +RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) { + POINT p; + GetCursorPos(&p); + if (x) *x = p.x; + if (y) *y = p.y; + return RGFW_TRUE; +} + +void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->src.aspectRatioW = w; + win->src.aspectRatioH = h; +} + +void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->src.minSizeW = w; + win->src.minSizeH = h; +} + +void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + win->src.maxSizeW = w; + win->src.maxSizeH = h; +} + +void RGFW_window_focus(RGFW_window* win) { + RGFW_ASSERT(win); + SetForegroundWindow(win->src.window); + SetFocus(win->src.window); +} + +void RGFW_window_raise(RGFW_window* win) { + RGFW_ASSERT(win); + BringWindowToTop(win->src.window); + SetWindowPos(win->src.window, HWND_TOP, win->x, win->y, win->w, win->h, SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); +} + +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + + if (fullscreen == RGFW_FALSE) { + RGFW_window_setBorder(win, 1); + SetWindowPos(win->src.window, HWND_NOTOPMOST, win->internal.oldX, win->internal.oldY, win->internal.oldW + (i32)win->src.offsetW, win->internal.oldH + (i32)win->src.offsetH, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + + win->internal.flags &= ~(u32)RGFW_windowFullscreen; + win->x = win->internal.oldX; + win->y = win->internal.oldY; + win->w = win->internal.oldW; + win->h = win->internal.oldH; + return; + } + + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + win->internal.flags |= RGFW_windowFullscreen; + + RGFW_monitor mon = RGFW_window_getMonitor(win); + RGFW_window_setBorder(win, 0); + + SetWindowPos(win->src.window, HWND_TOPMOST, (i32)mon.x, (i32)mon.x, (i32)mon.mode.w, (i32)mon.mode.h, SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW); + RGFW_monitor_scaleToWindow(mon, win); + + win->x = mon.x; win->y = mon.x; + win->w = mon.mode.w; + win->h = mon.mode.h; +} + +void RGFW_window_maximize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_window_hide(win); + ShowWindow(win->src.window, SW_MAXIMIZE); +} + +void RGFW_window_minimize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + ShowWindow(win->src.window, SW_MINIMIZE); +} + +void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { + RGFW_ASSERT(win != NULL); + if (floating) SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + else SetWindowPos(win->src.window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); +} + +void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { + SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED); + SetLayeredWindowAttributes(win->src.window, 0, opacity, LWA_ALPHA); +} + +void RGFW_window_restore(RGFW_window* win) { RGFW_window_show(win); } + +RGFW_bool RGFW_window_isFloating(RGFW_window* win) { + return (GetWindowLongPtr(win->src.window, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0; +} + +void RGFW_stopCheckEvents(void) { + PostMessageW(_RGFW->root->src.window, WM_NULL, 0, 0); +} + +void RGFW_waitForEvent(i32 waitMS) { + MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)waitMS, QS_ALLINPUT); +} + +u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) { + UINT vsc = RGFW_rgfwToApiKey(rgfw_keycode); /* Should return a Windows VK_* code */ + BYTE keyboardState[256] = {0}; + + if (!GetKeyboardState(keyboardState)) + return (u8)rgfw_keycode; + + UINT vk = MapVirtualKeyW(vsc, MAPVK_VSC_TO_VK); + HKL layout = GetKeyboardLayout(0); + + wchar_t charBuffer[2] = {0}; + int result = ToUnicodeEx(vk, vsc, keyboardState, charBuffer, 1, 0, layout); + + if (result <= 0) + return (u8)rgfw_keycode; + + return (u8)charBuffer[0]; +} + +void RGFW_pollEvents(void) { + RGFW_resetPrevState(); + MSG msg; + while (PeekMessageA(&msg, NULL, 0u, 0u, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } +} + +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win); +} + +RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + #ifndef __cplusplus + WINDOWPLACEMENT placement = {0}; + #else + WINDOWPLACEMENT placement = {}; + #endif + GetWindowPlacement(win->src.window, &placement); + return placement.showCmd == SW_SHOWMINIMIZED; +} + +RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + #ifndef __cplusplus + WINDOWPLACEMENT placement = {0}; + #else + WINDOWPLACEMENT placement = {}; + #endif + GetWindowPlacement(win->src.window, &placement); + return placement.showCmd == SW_SHOWMAXIMIZED || IsZoomed(win->src.window); +} + +typedef struct { int iIndex; HMONITOR hMonitor; RGFW_monitor* monitors; } RGFW_mInfo; +#ifndef RGFW_NO_MONITOR +RGFW_monitor RGFW_win32_createMonitor(HMONITOR src); +RGFW_monitor RGFW_win32_createMonitor(HMONITOR src) { + RGFW_monitor monitor; + RGFW_MEMSET(&monitor, 0, sizeof(monitor)); + + MONITORINFOEXW monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFOEXW); + GetMonitorInfoW(src, (LPMONITORINFO)&monitorInfo); + + /* get the monitor's index */ + DISPLAY_DEVICEW dd; + dd.cb = sizeof(dd); + + DWORD deviceNum; + for (deviceNum = 0; EnumDisplayDevicesW(NULL, deviceNum, &dd, 0); deviceNum++) { + if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + DEVMODEW dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (EnumDisplaySettingsW(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) { + monitor.mode.refreshRate = dm.dmDisplayFrequency; + RGFW_splitBPP(dm.dmBitsPerPel, &monitor.mode); + } + + DISPLAY_DEVICEW mdd; + mdd.cb = sizeof(mdd); + + if (EnumDisplayDevicesW(dd.DeviceName, (DWORD)deviceNum, &mdd, 0)) { + RGFW_createUTF8FromWideStringWin32(mdd.DeviceString, monitor.name, sizeof(monitor.name)); + monitor.name[sizeof(monitor.name) - 1] = '\0'; + break; + } + } + + monitor.x = monitorInfo.rcWork.left; + monitor.y = monitorInfo.rcWork.top; + monitor.mode.w = (i32)(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left); + monitor.mode.h = (i32)(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top); + + HDC hdc = CreateDCW(monitorInfo.szDevice, NULL, NULL, NULL); + /* get pixels per inch */ + float dpiX = (float)GetDeviceCaps(hdc, LOGPIXELSX); + float dpiY = (float)GetDeviceCaps(hdc, LOGPIXELSX); + + monitor.scaleX = dpiX / 96.0f; + monitor.scaleY = dpiY / 96.0f; + monitor.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f; + + monitor.physW = (float)GetDeviceCaps(hdc, HORZSIZE) / 25.4f; + monitor.physH = (float)GetDeviceCaps(hdc, VERTSIZE) / 25.4f; + DeleteDC(hdc); + + #ifndef RGFW_NO_DPI + RGFW_LOAD_LIBRARY(RGFW_Shcore_dll, "shcore.dll"); + RGFW_PROC_DEF(RGFW_Shcore_dll, GetDpiForMonitor); + + if (GetDpiForMonitor != NULL) { + u32 x, y; + GetDpiForMonitor(src, MDT_EFFECTIVE_DPI, &x, &y); + monitor.scaleX = (float) (x) / (float) 96.0f; + monitor.scaleY = (float) (y) / (float) 96.0f; + monitor.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f; + } + #endif + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, "monitor found"); + return monitor; +} +#endif /* RGFW_NO_MONITOR */ + +#ifndef RGFW_NO_MONITOR +BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData); +BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + RGFW_UNUSED(hdcMonitor); + RGFW_UNUSED(lprcMonitor); + + RGFW_mInfo* info = (RGFW_mInfo*) dwData; + + + if (info->iIndex >= RGFW_MAX_MONITORS) + return FALSE; + + info->monitors[info->iIndex] = RGFW_win32_createMonitor(hMonitor); + info->iIndex++; + + return TRUE; +} + +RGFW_monitor RGFW_getPrimaryMonitor(void) { + #ifdef __cplusplus + return RGFW_win32_createMonitor(MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY)); + #else + return RGFW_win32_createMonitor(MonitorFromPoint((POINT){0, 0}, MONITOR_DEFAULTTOPRIMARY)); + #endif +} + +RGFW_monitor* RGFW_getMonitors(size_t* len) { + static RGFW_monitor monitors[RGFW_MAX_MONITORS]; + RGFW_mInfo info; + info.iIndex = 0; + info.monitors = monitors; + + EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info); + + if (len != NULL) *len = (size_t)info.iIndex; + return monitors; +} + +RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY); + return RGFW_win32_createMonitor(src); +} + +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { + POINT p = { mon.x, mon.y }; + HMONITOR src = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY); + + MONITORINFOEX monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo); + + DISPLAY_DEVICEA dd; + dd.cb = sizeof(dd); + + /* Enumerate display devices */ + DWORD deviceNum; + for (deviceNum = 0; EnumDisplayDevicesA(NULL, deviceNum, &dd, 0); deviceNum++) { + if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + if (strcmp(dd.DeviceName, (const char*)monitorInfo.szDevice) != 0) + continue; + + DEVMODEA dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) { + if (request & RGFW_monitorScale) { + dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT; + dm.dmPelsWidth = (u32)mode.w; + dm.dmPelsHeight = (u32)mode.h; + } + + if (request & RGFW_monitorRefresh) { + dm.dmFields |= DM_DISPLAYFREQUENCY; + dm.dmDisplayFrequency = mode.refreshRate; + } + + if (request & RGFW_monitorRGB) { + dm.dmFields |= DM_BITSPERPEL; + dm.dmBitsPerPel = (DWORD)(mode.red + mode.green + mode.blue); + } + + if (ChangeDisplaySettingsExA(dd.DeviceName, &dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) { + if (ChangeDisplaySettingsExA(dd.DeviceName, &dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL) + return RGFW_TRUE; + return RGFW_FALSE; + } else return RGFW_FALSE; + } + } + + return RGFW_FALSE; +} + +#endif +HICON RGFW_loadHandleImage(u8* data, i32 w, i32 h, RGFW_format format, BOOL icon); +HICON RGFW_loadHandleImage(u8* data, i32 w, i32 h, RGFW_format format, BOOL icon) { + BITMAPV5HEADER bi; + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(bi); + bi.bV5Width = (i32)w; + bi.bV5Height = -((LONG) h); + bi.bV5Planes = 1; + bi.bV5BitCount = (WORD)32; + bi.bV5Compression = BI_RGB; + HDC dc = GetDC(NULL); + u8* target = NULL; + + HBITMAP color = CreateDIBSection(dc, + (BITMAPINFO*) &bi, DIB_RGB_COLORS, (void**) &target, + NULL, (DWORD) 0); + + RGFW_copyImageData(target, w, h, RGFW_formatBGRA8, data, format); + ReleaseDC(NULL, dc); + + HBITMAP mask = CreateBitmap((i32)w, (i32)h, 1, 1, NULL); + + ICONINFO ii; + ZeroMemory(&ii, sizeof(ii)); + ii.fIcon = icon; + ii.xHotspot = (u32)w / 2; + ii.yHotspot = (u32)h / 2; + ii.hbmMask = mask; + ii.hbmColor = color; + + HICON handle = CreateIconIndirect(&ii); + + DeleteObject(color); + DeleteObject(mask); + + return handle; +} +RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) { + HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(data, w, h, format, FALSE); + return cursor; +} + +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { + RGFW_ASSERT(win && mouse); + SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) mouse); + SetCursor((HCURSOR)mouse); +} + +void RGFW_freeMouse(RGFW_mouse* mouse) { + RGFW_ASSERT(mouse); + DestroyCursor((HCURSOR)mouse); +} + +RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} + +RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { + RGFW_ASSERT(win != NULL); + + u32 mouseIcon = 0; + + switch (mouse) { + case RGFW_mouseNormal: mouseIcon = OCR_NORMAL; break; + case RGFW_mouseArrow: mouseIcon = OCR_NORMAL; break; + case RGFW_mouseIbeam: mouseIcon = OCR_IBEAM; break; + case RGFW_mouseWait: mouseIcon = OCR_WAIT; break; + case RGFW_mouseCrosshair: mouseIcon = OCR_CROSS; break; + case RGFW_mouseProgress: mouseIcon = OCR_APPSTARTING; break; + case RGFW_mouseResizeNWSE: mouseIcon = OCR_SIZENWSE; break; + case RGFW_mouseResizeNESW: mouseIcon = OCR_SIZENESW; break; + case RGFW_mouseResizeEW: mouseIcon = OCR_SIZEWE; break; + case RGFW_mouseResizeNS: mouseIcon = OCR_SIZENS; break; + case RGFW_mouseResizeAll: mouseIcon = OCR_SIZEALL; break; + case RGFW_mouseNotAllowed: mouseIcon = OCR_NO; break; + case RGFW_mousePointingHand: mouseIcon = OCR_HAND; break; + case RGFW_mouseResizeNW: mouseIcon = OCR_SIZENWSE; break; + case RGFW_mouseResizeN: mouseIcon = OCR_SIZENS; break; + case RGFW_mouseResizeNE: mouseIcon = OCR_SIZENESW; break; + case RGFW_mouseResizeE: mouseIcon = OCR_SIZEWE; break; + case RGFW_mouseResizeSE: mouseIcon = OCR_SIZENWSE; break; + case RGFW_mouseResizeS: mouseIcon = OCR_SIZENS; break; + case RGFW_mouseResizeSW: mouseIcon = OCR_SIZENESW; break; + case RGFW_mouseResizeW: mouseIcon = OCR_SIZEWE; break; + default: return RGFW_FALSE; + } + + char* icon = MAKEINTRESOURCEA(mouseIcon); + + SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon)); + SetCursor(LoadCursorA(NULL, icon)); + return RGFW_TRUE; +} + +void RGFW_window_hide(RGFW_window* win) { + ShowWindow(win->src.window, SW_HIDE); +} + +void RGFW_window_show(RGFW_window* win) { + if (win->internal.flags & RGFW_windowFocusOnShow) RGFW_window_focus(win); + ShowWindow(win->src.window, SW_RESTORE); +} + +void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { + if (RGFW_window_isInFocus(win) && request) { + return; + } + + FLASHWINFO desc; + RGFW_MEMSET(&desc, 0, sizeof(desc)); + + desc.cbSize = sizeof(desc); + desc.hwnd = win->src.window; + + switch (request) { + case RGFW_flashCancel: + desc.dwFlags = FLASHW_STOP; + break; + case RGFW_flashBriefly: + desc.dwFlags = FLASHW_TRAY; + desc.uCount = 1; + break; + case RGFW_flashUntilFocused: + desc.dwFlags = (FLASHW_TRAY | FLASHW_TIMERNOFG); + break; + default: break; + } + + FlashWindowEx(&desc); +} + +#define RGFW_FREE_LIBRARY(x) if (x != NULL) FreeLibrary(x); x = NULL; +void RGFW_deinitPlatform(void) { + #ifndef RGFW_NO_DPI + RGFW_FREE_LIBRARY(RGFW_Shcore_dll); + #endif + + #ifndef RGFW_NO_WINMM + timeEndPeriod(1); + #ifndef RGFW_NO_LOAD_WINMM + RGFW_FREE_LIBRARY(RGFW_winmm_dll); + #endif + #endif + + RGFW_FREE_LIBRARY(RGFW_wgl_dll); + + RGFW_freeMouse(_RGFW->hiddenMouse); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, "global context deinitialized"); +} + + +void RGFW_window_closePlatform(RGFW_window* win) { + RemovePropW(win->src.window, L"RGFW"); + ReleaseDC(win->src.window, win->src.hdc); /*!< delete device context */ + DestroyWindow(win->src.window); /*!< delete window */ + + if (win->src.hIconSmall) DestroyIcon(win->src.hIconSmall); + if (win->src.hIconBig) DestroyIcon(win->src.hIconBig); +} + +void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + + win->x = x; + win->y = y; + SetWindowPos(win->src.window, HWND_TOP, win->x, win->y, 0, 0, SWP_NOSIZE); +} + +void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + win->w = w; + win->h = h; + SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->w + (i32)win->src.offsetW, win->h + (i32)win->src.offsetH, SWP_NOMOVE); +} + + +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + if (name == NULL) name = "\0"; + + wchar_t wide_name[256]; + MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 256); + SetWindowTextW(win->src.window, wide_name); +} + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { + RGFW_ASSERT(win != NULL); + COLORREF key = 0; + BYTE alpha = 0; + DWORD flags = 0; + i32 exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE); + + if (exStyle & WS_EX_LAYERED) + GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags); + + if (passthrough) + exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED); + else { + exStyle &= ~WS_EX_TRANSPARENT; + if (exStyle & WS_EX_LAYERED && !(flags & LWA_ALPHA)) + exStyle &= ~WS_EX_LAYERED; + } + + SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle); + + if (passthrough) + SetLayeredWindowAttributes(win->src.window, key, alpha, flags); +} +#endif + +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { + RGFW_ASSERT(win != NULL); + #ifndef RGFW_WIN95 + if (win->src.hIconSmall && (type & RGFW_iconWindow)) DestroyIcon(win->src.hIconSmall); + if (win->src.hIconBig && (type & RGFW_iconTaskbar)) DestroyIcon(win->src.hIconBig); + + if (data == NULL) { + HICON defaultIcon = LoadIcon(NULL, IDI_APPLICATION); + if (type & RGFW_iconWindow) + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)defaultIcon); + if (type & RGFW_iconTaskbar) + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)defaultIcon); + return RGFW_TRUE; + } + + if (type & RGFW_iconWindow) { + win->src.hIconSmall = RGFW_loadHandleImage(data, w, h, format, TRUE); + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)win->src.hIconSmall); + } + if (type & RGFW_iconTaskbar) { + win->src.hIconBig = RGFW_loadHandleImage(data, w, h, format, TRUE); + SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)win->src.hIconBig); + } + return RGFW_TRUE; + #else + RGFW_UNUSED(img); + RGFW_UNUSED(type); + return RGFW_FALSE; + #endif +} + +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { + /* Open the clipboard */ + if (OpenClipboard(NULL) == 0) + return -1; + + /* Get the clipboard data as a Unicode string */ + HANDLE hData = GetClipboardData(CF_UNICODETEXT); + if (hData == NULL) { + CloseClipboard(); + return -1; + } + + wchar_t* wstr = (wchar_t*) GlobalLock(hData); + + RGFW_ssize_t textLen = 0; + + { + setlocale(LC_ALL, "en_US.UTF-8"); + + textLen = (RGFW_ssize_t)wcstombs(NULL, wstr, 0) + 1; + if (str != NULL && (RGFW_ssize_t)strCapacity <= textLen - 1) + textLen = 0; + + if (str != NULL && textLen) { + if (textLen > 1) + wcstombs(str, wstr, (size_t)(textLen)); + + str[textLen - 1] = '\0'; + } + } + + /* Release the clipboard data */ + GlobalUnlock(hData); + CloseClipboard(); + + return textLen; +} + +void RGFW_writeClipboard(const char* text, u32 textLen) { + HANDLE object; + WCHAR* buffer; + + object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR)); + if (!object) + return; + + buffer = (WCHAR*) GlobalLock(object); + if (!buffer) { + GlobalFree(object); + return; + } + + MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, (i32)textLen); + GlobalUnlock(object); + + if (!OpenClipboard(_RGFW->root->src.window)) { + GlobalFree(object); + return; + } + + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, object); + CloseClipboard(); +} + +void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + win->internal.lastMouseX = x - win->x; + win->internal.lastMouseY = y - win->y; + SetCursorPos(x, y); +} + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char * extension, size_t len) { + const char* extensions = NULL; + + RGFW_proc proc = RGFW_getProcAddress_OpenGL("wglGetExtensionsStringARB"); + RGFW_proc proc2 = RGFW_getProcAddress_OpenGL("wglGetExtensionsStringEXT"); + + if (proc) + extensions = ((const char* (*)(HDC))proc)(wglGetCurrentDC()); + else if (proc2) + extensions = ((const char*(*)(void))proc2)(); + return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len); +} + +RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) { + RGFW_proc proc = (RGFW_proc)wglGetProcAddress(procname); + if (proc) + return proc; + + return (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname); +} + +void RGFW_win32_loadOpenGLFuncs(HWND dummyWin) { + if (wglSwapIntervalEXT != NULL && wglChoosePixelFormatARB != NULL && wglChoosePixelFormatARB != NULL) + return; + + HDC dummy_dc = GetDC(dummyWin); + u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + + PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 32, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 32, 8, 0, PFD_MAIN_PLANE, 0, 0, 0, 0}; + + int dummy_pixel_format = ChoosePixelFormat(dummy_dc, &pfd); + SetPixelFormat(dummy_dc, dummy_pixel_format, &pfd); + + HGLRC dummy_context = wglCreateContext(dummy_dc); + + HGLRC cur = wglGetCurrentContext(); + wglMakeCurrent(dummy_dc, dummy_context); + + wglCreateContextAttribsARB = ((PFNWGLCREATECONTEXTATTRIBSARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglCreateContextAttribsARB"); + wglChoosePixelFormatARB = ((PFNWGLCHOOSEPIXELFORMATARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglChoosePixelFormatARB"); + + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(RGFW_proc)wglGetProcAddress("wglSwapIntervalEXT"); + if (wglSwapIntervalEXT == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load swap interval function"); + } + + wglMakeCurrent(dummy_dc, cur); + wglDeleteContext(dummy_context); + ReleaseDC(dummyWin, dummy_dc); +} + +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202b +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_ALPHA_BITS_ARB 0x201b +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_STEREO_ARB 0x2012 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ACCUM_RED_BITS_ARB 0x201e +#define WGL_ACCUM_GREEN_BITS_ARB 0x201f +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_COLORSPACE_SRGB_EXT 0x3089 +#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0x0000 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_ACCESS_READ_WRITE_NV 0x00000001 +#define WGL_COVERAGE_SAMPLES_NV 0x2042 +#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 +#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_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 + +RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { + const char flushControl[] = "WGL_ARB_context_flush_control"; + const char noError[] = "WGL_ARB_create_context_no_error"; + const char robustness[] = "WGL_ARB_create_context_robustness"; + + win->src.ctx.native = ctx; + win->src.gfxType = RGFW_gfxNativeOpenGL; + + PIXELFORMATDESCRIPTOR pfd; + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.iLayerType = PFD_MAIN_PLANE; + pfd.cColorBits = 32; + pfd.cAlphaBits = 8; + pfd.cDepthBits = 24; + pfd.cStencilBits = (BYTE)hints->stencil; + pfd.cAuxBuffers = (BYTE)hints->auxBuffers; + if (hints->stereo) pfd.dwFlags |= PFD_STEREO; + + /* try to create the pixel format we want for OpenGL and then try to create an OpenGL context for the specified version */ + if (hints->renderer == RGFW_glSoftware) + pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED; + + /* get pixel format, default to a basic pixel format */ + int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd); + if (wglChoosePixelFormatARB != NULL) { + i32 pixel_format_attribs[50]; + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, pixel_format_attribs, 50); + + RGFW_attribStack_pushAttribs(&stack, WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB); + RGFW_attribStack_pushAttribs(&stack, WGL_DRAW_TO_WINDOW_ARB, 1); + RGFW_attribStack_pushAttribs(&stack, WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB); + RGFW_attribStack_pushAttribs(&stack, WGL_SUPPORT_OPENGL_ARB, 1); + RGFW_attribStack_pushAttribs(&stack, WGL_COLOR_BITS_ARB, 32); + RGFW_attribStack_pushAttribs(&stack, WGL_DOUBLE_BUFFER_ARB, 1); + RGFW_attribStack_pushAttribs(&stack, WGL_ALPHA_BITS_ARB, hints->alpha); + RGFW_attribStack_pushAttribs(&stack, WGL_DEPTH_BITS_ARB, hints->depth); + RGFW_attribStack_pushAttribs(&stack, WGL_STENCIL_BITS_ARB, hints->stencil); + RGFW_attribStack_pushAttribs(&stack, WGL_STEREO_ARB, hints->stereo); + RGFW_attribStack_pushAttribs(&stack, WGL_AUX_BUFFERS_ARB, hints->auxBuffers); + RGFW_attribStack_pushAttribs(&stack, WGL_RED_BITS_ARB, hints->red); + RGFW_attribStack_pushAttribs(&stack, WGL_GREEN_BITS_ARB, hints->blue); + RGFW_attribStack_pushAttribs(&stack, WGL_BLUE_BITS_ARB, hints->green); + RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_RED_BITS_ARB, hints->accumRed); + RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_GREEN_BITS_ARB, hints->accumGreen); + RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_BLUE_BITS_ARB, hints->accumBlue); + RGFW_attribStack_pushAttribs(&stack, WGL_ACCUM_ALPHA_BITS_ARB, hints->accumAlpha); + + if(hints->sRGB) { + if (hints->profile != RGFW_glES) + RGFW_attribStack_pushAttribs(&stack, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, 1); + else + RGFW_attribStack_pushAttribs(&stack, WGL_COLORSPACE_SRGB_EXT, hints->sRGB); + } + + RGFW_attribStack_pushAttribs(&stack, WGL_COVERAGE_SAMPLES_NV, hints->samples); + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + + int new_pixel_format; + UINT num_formats; + wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &new_pixel_format, &num_formats); + if (!num_formats) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create a pixel format for WGL"); + else pixel_format = new_pixel_format; + } + + PIXELFORMATDESCRIPTOR suggested; + if (!DescribePixelFormat(win->src.hdc, pixel_format, sizeof(suggested), &suggested) || + !SetPixelFormat(win->src.hdc, pixel_format, &pfd)) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to set the WGL pixel format"); + + if (wglCreateContextAttribsARB != NULL) { + /* create OpenGL/WGL context for the specified version */ + i32 attribs[40]; + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, attribs, 50); + + + i32 mask = 0; + switch (hints->profile) { + case RGFW_glES: mask |= WGL_CONTEXT_ES_PROFILE_BIT_EXT; break; + case RGFW_glCompatibility: mask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; break; + case RGFW_glForwardCompatibility: mask |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; break; + case RGFW_glCore: mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; break; + default: mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; break; + } + + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_PROFILE_MASK_ARB, mask); + + if (hints->minor || hints->major) { + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_MAJOR_VERSION_ARB, hints->major); + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_MINOR_VERSION_ARB, hints->minor); + } + + if (RGFW_extensionSupportedPlatform_OpenGL(noError, sizeof(noError))) + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_OPENGL_NO_ERROR_ARB, hints->noError); + + if (RGFW_extensionSupportedPlatform_OpenGL(flushControl, sizeof(flushControl))) { + if (hints->releaseBehavior == RGFW_glReleaseFlush) { + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); /* WGL_CONTEXT_RELEASE_BEHAVIOR_ARB */ + } else if (hints->releaseBehavior == RGFW_glReleaseNone) { + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + } + } + + i32 flags = 0; + if (hints->debug) flags |= WGL_CONTEXT_DEBUG_BIT_ARB; + if (hints->robustness && RGFW_extensionSupportedPlatform_OpenGL(robustness, sizeof(robustness))) flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; + if (flags) { + RGFW_attribStack_pushAttribs(&stack, WGL_CONTEXT_FLAGS_ARB, flags); + } + + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + + win->src.ctx.native->ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs); + } + + if (wglCreateContextAttribsARB == NULL || win->src.ctx.native->ctx == NULL) { /* fall back to a default context (probably OpenGL 2 or something) */ + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to create an accelerated OpenGL Context."); + win->src.ctx.native->ctx = wglCreateContext(win->src.hdc); + } + + ReleaseDC(win->src.window, win->src.hdc); + win->src.hdc = GetDC(win->src.window); + + if (hints->share) { + wglShareLists((HGLRC)RGFW_getCurrentContext_OpenGL(), hints->share->ctx); + } + + wglMakeCurrent(win->src.hdc, win->src.ctx.native->ctx); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized."); + return RGFW_TRUE; +} + +void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { + wglDeleteContext((HGLRC) ctx->ctx); /*!< delete OpenGL context */ + win->src.ctx.native->ctx = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed."); +} + +void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { + if (win == NULL) + wglMakeCurrent(NULL, NULL); + else + wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx.native->ctx); +} +void* RGFW_getCurrentContext_OpenGL(void) { + return wglGetCurrentContext(); +} +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { + RGFW_ASSERT(win->src.ctx.native); + SwapBuffers(win->src.hdc); +} + +void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); + if (wglSwapIntervalEXT == NULL || wglSwapIntervalEXT(swapInterval) == FALSE) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to set swap interval"); +} +#endif + +RGFW_bool RGFW_createUTF8FromWideStringWin32(const WCHAR* source, char* output, size_t max) { + i32 size = 0; + if (source == NULL) { + return RGFW_FALSE; + } + size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); + if (!size) { + return RGFW_FALSE; + } + + if (size > (i32)max) + size = (i32)max; + + if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, output, size, NULL, NULL)) { + return RGFW_FALSE; + } + + output[size] = 0; + return RGFW_TRUE; +} + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + WGPUSurfaceSourceWindowsHWND fromHwnd = {0}; + fromHwnd.chain.sType = WGPUSType_SurfaceSourceWindowsHWND; + fromHwnd.hwnd = window->src.window; + + fromHwnd.hinstance = GetModuleHandle(NULL); + + surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromHwnd.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#endif + +#endif /* RGFW_WINDOWS */ + +/* + End of Windows defines +*/ + + + +/* + + Start of MacOS defines + + +*/ + +#if defined(RGFW_MACOS) +/* + based on silicon.h + start of cocoa wrapper +*/ + +#include +#include +#include +#include +#include +#include + +#ifndef __OBJC__ +typedef CGRect NSRect; +typedef CGPoint NSPoint; +typedef CGSize NSSize; + +typedef const char* NSPasteboardType; +typedef unsigned long NSUInteger; +typedef long NSInteger; +typedef NSInteger NSModalResponse; + +typedef enum NSRequestUserAttentionType { + NSCriticalRequest = 0, + NSInformationalRequest = 10 +} NSRequestUserAttentionType; + +typedef enum NSApplicationActivationPolicy { + NSApplicationActivationPolicyRegular, + NSApplicationActivationPolicyAccessory, + NSApplicationActivationPolicyProhibited +} NSApplicationActivationPolicy; + +typedef RGFW_ENUM(u32, NSBackingStoreType) { + NSBackingStoreRetained = 0, + NSBackingStoreNonretained = 1, + NSBackingStoreBuffered = 2 +}; + +typedef RGFW_ENUM(u32, NSWindowStyleMask) { + NSWindowStyleMaskBorderless = 0, + NSWindowStyleMaskTitled = 1 << 0, + NSWindowStyleMaskClosable = 1 << 1, + NSWindowStyleMaskMiniaturizable = 1 << 2, + NSWindowStyleMaskResizable = 1 << 3, + NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */ + NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12, + NSWindowStyleMaskFullScreen = 1 << 14, + NSWindowStyleMaskFullSizeContentView = 1 << 15, + NSWindowStyleMaskUtilityWindow = 1 << 4, + NSWindowStyleMaskDocModalWindow = 1 << 6, + NSWindowStyleMaskNonactivatingpanel = 1 << 7, + NSWindowStyleMaskHUDWindow = 1 << 13 +}; + +#define NSPasteboardTypeString "public.utf8-plain-text" + +typedef RGFW_ENUM(i32, NSDragOperation) { + NSDragOperationNone = 0, + NSDragOperationCopy = 1, + NSDragOperationLink = 2, + NSDragOperationGeneric = 4, + NSDragOperationPrivate = 8, + NSDragOperationMove = 16, + NSDragOperationDelete = 32, + NSDragOperationEvery = (int)ULONG_MAX +}; + +typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) { + NSOpenGLContextParameterSwapInterval = 222, /* 1 param. 0 -> Don't sync, 1 -> Sync to vertical retrace */ + NSOpenGLContextParametectxaceOrder = 235, /* 1 param. 1 -> Above Window (default), -1 -> Below Window */ + NSOpenGLContextParametectxaceOpacity = 236, /* 1 param. 1-> Surface is opaque (default), 0 -> non-opaque */ + NSOpenGLContextParametectxaceBackingSize = 304, /* 2 params. Width/height of surface backing size */ + NSOpenGLContextParameterReclaimResources = 308, /* 0 params. */ + NSOpenGLContextParameterCurrentRendererID = 309, /* 1 param. Retrieves the current renderer ID */ + NSOpenGLContextParameterGPUVertexProcessing = 310, /* 1 param. Currently processing vertices with GPU (get) */ + NSOpenGLContextParameterGPUFragmentProcessing = 311, /* 1 param. Currently processing fragments with GPU (get) */ + NSOpenGLContextParameterHasDrawable = 314, /* 1 param. Boolean returned if drawable is attached */ + NSOpenGLContextParameterMPSwapsInFlight = 315, /* 1 param. Max number of swaps queued by the MP GL engine */ + + NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params. Set or get the swap rectangle {x, y, w, h} */ + NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */ + NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */ + NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */ + NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param. Surface volatile state */ +}; + +typedef RGFW_ENUM(NSInteger, NSWindowButton) { + NSWindowCloseButton = 0, + NSWindowMiniaturizeButton = 1, + NSWindowZoomButton = 2, + NSWindowToolbarButton = 3, + NSWindowDocumentIconButton = 4, + NSWindowDocumentVersionsButton = 6, + NSWindowFullScreenButton = 7, +}; + +#define NSPasteboardTypeURL "public.url" +#define NSPasteboardTypeFileURL "public.file-url" +#define NSTrackingMouseEnteredAndExited 0x01 +#define NSTrackingMouseMoved 0x02 +#define NSTrackingCursorUpdate 0x04 +#define NSTrackingActiveWhenFirstResponder 0x10 +#define NSTrackingActiveInKeyWindow 0x20 +#define NSTrackingActiveInActiveApp 0x40 +#define NSTrackingActiveAlways 0x80 +#define NSTrackingAssumeInside 0x100 +#define NSTrackingInVisibleRect 0x200 +#define NSTrackingEnabledDuringMouseDrag 0x400 +enum { + NSOpenGLPFAAllRenderers = 1, /* choose from all available renderers */ + NSOpenGLPFATripleBuffer = 3, /* choose a triple buffered pixel format */ + NSOpenGLPFADoubleBuffer = 5, /* choose a double buffered pixel format */ + NSOpenGLPFAAuxBuffers = 7, /* number of aux buffers */ + NSOpenGLPFAColorSize = 8, /* number of color buffer bits */ + NSOpenGLPFAAlphaSize = 11, /* number of alpha component bits */ + NSOpenGLPFADepthSize = 12, /* number of depth buffer bits */ + NSOpenGLPFAStencilSize = 13, /* number of stencil buffer bits */ + NSOpenGLPFAAccumSize = 14, /* number of accum buffer bits */ + NSOpenGLPFAMinimumPolicy = 51, /* never choose smaller buffers than requested */ + NSOpenGLPFAMaximumPolicy = 52, /* choose largest buffers of type requested */ + NSOpenGLPFASampleBuffers = 55, /* number of multi sample buffers */ + NSOpenGLPFASamples = 56, /* number of samples per multi sample buffer */ + NSOpenGLPFAAuxDepthStencil = 57, /* each aux buffer has its own depth stencil */ + NSOpenGLPFAColorFloat = 58, /* color buffers store floating point pixels */ + NSOpenGLPFAMultisample = 59, /* choose multisampling */ + NSOpenGLPFASupersample = 60, /* choose supersampling */ + NSOpenGLPFASampleAlpha = 61, /* request alpha filtering */ + NSOpenGLPFARendererID = 70, /* request renderer by ID */ + NSOpenGLPFANoRecovery = 72, /* disable all failure recovery systems */ + NSOpenGLPFAAccelerated = 73, /* choose a hardware accelerated renderer */ + NSOpenGLPFAClosestPolicy = 74, /* choose the closest color buffer to request */ + NSOpenGLPFABackingStore = 76, /* back buffer contents are valid after swap */ + NSOpenGLPFAScreenMask = 84, /* bit mask of supported physical screens */ + NSOpenGLPFAAllowOfflineRenderers = 96, /* allow use of offline renderers */ + NSOpenGLPFAAcceleratedCompute = 97, /* choose a hardware accelerated compute device */ + NSOpenGLPFAOpenGLProfile = 99, /* specify an OpenGL Profile to use */ + NSOpenGLProfileVersionLegacy = 0x1000, /* The requested profile is a legacy (pre-OpenGL 3.0) profile. */ + NSOpenGLProfileVersion3_2Core = 0x3200, /* The 3.2 Profile of OpenGL */ + NSOpenGLProfileVersion4_1Core = 0x3200, /* The 4.1 profile of OpenGL */ + NSOpenGLPFAVirtualScreenCount = 128, /* number of virtual screens in this format */ + NSOpenGLPFAStereo = 6, + NSOpenGLPFAOffScreen = 53, + NSOpenGLPFAFullScreen = 54, + NSOpenGLPFASingleRenderer = 71, + NSOpenGLPFARobust = 75, + NSOpenGLPFAMPSafe = 78, + NSOpenGLPFAWindow = 80, + NSOpenGLPFAMultiScreen = 81, + NSOpenGLPFACompliant = 83, + NSOpenGLPFAPixelBuffer = 90, + NSOpenGLPFARemotePixelBuffer = 91, +}; + +typedef RGFW_ENUM(u32, NSEventType) { /* various types of events */ + NSEventTypeApplicationDefined = 15, +}; +typedef unsigned long long NSEventMask; + +typedef enum NSEventModifierFlags { + NSEventModifierFlagCapsLock = 1 << 16, + NSEventModifierFlagShift = 1 << 17, + NSEventModifierFlagControl = 1 << 18, + NSEventModifierFlagOption = 1 << 19, + NSEventModifierFlagCommand = 1 << 20, + NSEventModifierFlagNumericPad = 1 << 21 +} NSEventModifierFlags; + +typedef RGFW_ENUM(NSUInteger, NSBitmapFormat) { + NSBitmapFormatAlphaFirst = 1 << 0, /* 0 means is alpha last (RGBA, CMYKA, etc.) */ + NSBitmapFormatAlphaNonpremultiplied = 1 << 1, /* 0 means is premultiplied */ + NSBitmapFormatFloatingpointSamples = 1 << 2, /* 0 is integer */ + + NSBitmapFormatSixteenBitLittleEndian = (1 << 8), + NSBitmapFormatThirtyTwoBitLittleEndian = (1 << 9), + NSBitmapFormatSixteenBitBigEndian = (1 << 10), + NSBitmapFormatThirtyTwoBitBigEndian = (1 << 11) +}; + +#else +#import +#include +#endif /* notdef __OBJC__ */ + +#ifdef __arm64__ + /* ARM just uses objc_msgSend */ +#define abi_objc_msgSend_stret objc_msgSend +#define abi_objc_msgSend_fpret objc_msgSend +#else /* __i386__ */ + /* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */ +#define abi_objc_msgSend_stret objc_msgSend_stret +#define abi_objc_msgSend_fpret objc_msgSend_fpret +#endif + +#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc")) +#define objc_msgSend_bool(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_void(x, y) ((void (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_void_id(x, y, z) ((void (*)(id, SEL, id))objc_msgSend) ((id)x, (SEL)y, (id)z) +#define objc_msgSend_uint(x, y) ((NSUInteger (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_void_bool(x, y, z) ((void (*)(id, SEL, BOOL))objc_msgSend) ((id)(x), (SEL)y, (BOOL)z) +#define objc_msgSend_bool_void(x, y) ((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_void_SEL(x, y, z) ((void (*)(id, SEL, SEL))objc_msgSend) ((id)(x), (SEL)y, (SEL)z) +#define objc_msgSend_id(x, y) ((id (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y) +#define objc_msgSend_id_id(x, y, z) ((id (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z) +#define objc_msgSend_id_bool(x, y, z) ((BOOL (*)(id, SEL, id))objc_msgSend) ((id)(x), (SEL)y, (id)z) +#define objc_msgSend_int(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z) +#define objc_msgSend_arr(x, y, z) ((id (*)(id, SEL, int))objc_msgSend) ((id)(x), (SEL)y, (int)z) +#define objc_msgSend_ptr(x, y, z) ((id (*)(id, SEL, void*))objc_msgSend) ((id)(x), (SEL)y, (void*)z) +#define objc_msgSend_class(x, y) ((id (*)(Class, SEL))objc_msgSend) ((Class)(x), (SEL)y) +#define objc_msgSend_class_char(x, y, z) ((id (*)(Class, SEL, char*))objc_msgSend) ((Class)(x), (SEL)y, (char*)z) + +#define NSRelease(obj) objc_msgSend_void((id)obj, sel_registerName("release")) +RGFWDEF id NSString_stringWithUTF8String(const char* str); +id NSString_stringWithUTF8String(const char* str) { + return ((id(*)(id, SEL, const char*))objc_msgSend) ((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str); +} + +const char* NSString_to_char(id str); +const char* NSString_to_char(id str) { + return ((const char* (*)(id, SEL)) objc_msgSend) ((id)(id)str, sel_registerName("UTF8String")); +} + +unsigned char* NSBitmapImageRep_bitmapData(id imageRep); +unsigned char* NSBitmapImageRep_bitmapData(id imageRep) { + return ((unsigned char* (*)(id, SEL))objc_msgSend) ((id)imageRep, sel_registerName("bitmapData")); +} + +id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits); +id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) { + SEL func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:"); + + return (id) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, id, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend) + (NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits); +} + +id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha); +id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) { + Class nsclass = objc_getClass("NSColor"); + SEL func = sel_registerName("colorWithSRGBRed:green:blue:alpha:"); + return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend) + ((id)nsclass, func, red, green, blue, alpha); +} + +id NSPasteboard_generalPasteboard(void); +id NSPasteboard_generalPasteboard(void) { + return (id) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard")); +} + +id* cstrToNSStringArray(char** strs, size_t len); +id* cstrToNSStringArray(char** strs, size_t len) { + static id nstrs[6]; + size_t i; + for (i = 0; i < len; i++) + nstrs[i] = NSString_stringWithUTF8String(strs[i]); + + return nstrs; +} + +const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len); +const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len) { + SEL func = sel_registerName("stringForType:"); + id nsstr = NSString_stringWithUTF8String((const char*)dataType); + id nsString = ((id(*)(id, SEL, id))objc_msgSend)(pasteboard, func, nsstr); + const char* str = NSString_to_char(nsString); + if (len != NULL) + *len = (size_t)((NSUInteger(*)(id, SEL, int))objc_msgSend)(nsString, sel_registerName("maximumLengthOfBytesUsingEncoding:"), 4); + return str; +} + +id c_array_to_NSArray(void* array, size_t len); +id c_array_to_NSArray(void* array, size_t len) { + return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend) (NSAlloc(objc_getClass("NSArray")), sel_registerName("initWithObjects:count:"), array, len); +} + + +void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len); +void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len) { + id* ntypes = cstrToNSStringArray((char**)newTypes, len); + + id array = c_array_to_NSArray(ntypes, len); + objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array); + NSRelease(array); +} + +NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner); +NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) { + id* ntypes = cstrToNSStringArray((char**)newTypes, len); + + SEL func = sel_registerName("declareTypes:owner:"); + + id array = c_array_to_NSArray(ntypes, len); + + NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend) + (pasteboard, func, array, owner); + NSRelease(array); + + return output; +} + +#define NSRetain(obj) objc_msgSend_void((id)obj, sel_registerName("retain")) + +/* + End of cocoa wrapper +*/ + +static id RGFW__osxCustomInitWithRGFWWindow(id self, SEL _cmd, RGFW_window* win) { + RGFW_UNUSED(_cmd); + struct objc_super s = { self, class_getSuperclass(object_getClass(self)) }; + self = ((id (*)(struct objc_super*, SEL))objc_msgSendSuper)(&s, sel_registerName("init")); + + if (self != nil) { + object_setInstanceVariable(self, "RGFW_window", win); + object_setInstanceVariable(self, "trackingArea", nil); + + object_setInstanceVariable( + self, "markedText", + ((id (*)(id, SEL))objc_msgSend)( + ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSMutableAttributedString"), sel_registerName("alloc")), + sel_registerName("init") + ) + ); + + ((void (*)(id, SEL))objc_msgSend)(self, sel_registerName("updateTrackingAreas")); + + ((void (*)(id, SEL, id))objc_msgSend)( + self, sel_registerName("registerForDraggedTypes:"), + ((id (*)(Class, SEL, id))objc_msgSend)( + objc_getClass("NSArray"), + sel_registerName("arrayWithObject:"), + ((id (*)(Class, SEL, const char*))objc_msgSend)( + objc_getClass("NSString"), + sel_registerName("stringWithUTF8String:"), + "public.url" + ) + ) + ); + } + + return self; +} + +static u32 RGFW_OnClose(id self) { + RGFW_window* win = NULL; + object_getInstanceVariable(self, (const char*)"RGFW_window", (void**)&win); + if (win == NULL) return true; + + RGFW_windowQuitCallback(win); + return false; +} + +/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */ +static bool RGFW__osxAcceptsFirstResponder(void) { return true; } +static bool RGFW__osxPerformKeyEquivalent(id event) { RGFW_UNUSED(event); return true; } + +static NSDragOperation RGFW__osxDraggingEntered(id self, SEL sel, id sender) { + RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); + + return NSDragOperationCopy; +} +static NSDragOperation RGFW__osxDraggingUpdated(id self, SEL sel, id sender) { + RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) + return 0; + if (!(win->internal.enabledEvents & RGFW_dataDragFlag)) return NSDragOperationCopy; + + NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation")); + RGFW_dataDragCallback(win, (i32) p.x, (i32) (win->h - p.y)); + return NSDragOperationCopy; +} +static bool RGFW__osxPrepareForDragOperation(id self) { + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL || (!(win->internal.enabledEvents & RGFW_dataDropFlag))) + return true; + + if (!(win->internal.flags & RGFW_windowAllowDND)) { + return false; + } + + return true; +} + +void RGFW__osxDraggingEnded(id self, SEL sel, id sender); +void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); return; } + +static bool RGFW__osxPerformDragOperation(id self, SEL sel, id sender) { + RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL || (!(win->internal.enabledEvents & RGFW_dataDropFlag))) + return false; + + /* id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); */ + + id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); + + /* Get the types of data available on the pasteboard */ + id types = objc_msgSend_id(pasteBoard, sel_registerName("types")); + + /* Get the string type for file URLs */ + id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType"); + + /* Check if the pasteboard contains file URLs */ + if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, "No files found on the pasteboard."); + return 0; + } + + id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType); + int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count")); + + if (count == 0) + return 0; + + char** files = (char**)(void*)_RGFW->files; + + u32 i; + for (i = 0; i < (u32)count; i++) { + id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i); + const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String")); + RGFW_STRNCPY(files[i], filePath, RGFW_MAX_PATH - 1); + files[i][RGFW_MAX_PATH - 1] = '\0'; + } + + RGFW_dataDropCallback(win, files, (size_t)count); + + return false; +} + +#ifndef RGFW_NO_IOKIT +#include + +u32 RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID); +u32 RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID) { + u32 refreshRate = 0; + io_iterator_t it; + io_service_t service; + CFNumberRef indexRef, clockRef, countRef; + u32 clock, count; + +#ifdef kIOMainPortDefault + if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0) +#elif defined(kIOMasterPortDefault) + if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0) +#endif + return RGFW_FALSE; + + while ((service = IOIteratorNext(it)) != 0) { + u32 index; + indexRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFramebufferOpenGLIndex"), kCFAllocatorDefault, kNilOptions); + if (indexRef == 0) continue; + + if (CFNumberGetValue(indexRef, kCFNumberIntType, &index) && CGOpenGLDisplayMaskToDisplayID(1 << index) == displayID) { + CFRelease(indexRef); + break; + } + + CFRelease(indexRef); + } + + if (service) { + clockRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelClock"), kCFAllocatorDefault, kNilOptions); + if (clockRef) { + if (CFNumberGetValue(clockRef, kCFNumberIntType, &clock) && clock) { + countRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelCount"), kCFAllocatorDefault, kNilOptions); + if (countRef && CFNumberGetValue(countRef, kCFNumberIntType, &count) && count) { + float rate = (float)((double)clock / (double) count); + refreshRate = (u32)RGFW_ROUND(rate); + CFRelease(countRef); + } + } + CFRelease(clockRef); + } + } + + IOObjectRelease(it); + return refreshRate; +} +#endif + +void RGFW_moveToMacOSResourceDir(void) { + char resourcesPath[256]; + + CFBundleRef bundle = CFBundleGetMainBundle(); + if (!bundle) + return; + + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); + CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); + + if ( + CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo || + CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0 + ) { + CFRelease(last); + CFRelease(resourcesURL); + return; + } + + CFRelease(last); + CFRelease(resourcesURL); + + chdir(resourcesPath); +} + + +static void RGFW__osxWindowDeminiaturize(id self, SEL sel) { + RGFW_UNUSED(sel); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h); + +} +static void RGFW__osxWindowMiniaturize(id self, SEL sel) { + RGFW_UNUSED(sel); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_windowMinimizedCallback(win); + +} + +static void RGFW__osxWindowBecameKey(id self, SEL sel) { + RGFW_UNUSED(sel); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_focusCallback(win, RGFW_TRUE); +} + +static void RGFW__osxWindowResignKey(id self, SEL sel) { + RGFW_UNUSED(sel); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_focusCallback(win, RGFW_FALSE); +} + +static void RGFW__osxDidWindowResize(id self, SEL _cmd, id notification) { + RGFW_UNUSED(_cmd); RGFW_UNUSED(notification); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + NSRect frame; + if (win->src.view) frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame")); + else return; + + if (frame.size.width == 0 || frame.size.height == 0) return; + win->w = (i32)frame.size.width; + win->h = (i32)frame.size.height; + + RGFW_monitor mon = RGFW_window_getMonitor(win); + if ((i32)mon.mode.w == win->w && (i32)mon.mode.h - 102 <= win->h) { + RGFW_windowMaximizedCallback(win, 0, 0, win->w, win->h); + } else if (win->internal.flags & RGFW_windowMaximize) { + RGFW_windowRestoredCallback(win, win->x, win->y, win->w, win->h); + } + + RGFW_windowResizedCallback(win, win->w, win->h); +} + +static void RGFW__osxWindowMove(id self, SEL sel) { + RGFW_UNUSED(sel); + + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame")); + RGFW_windowMovedCallback(win, (i32)frame.origin.x, (i32)frame.origin.y); +} + +static void RGFW__osxViewDidChangeBackingProperties(id self, SEL _cmd) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_monitor mon = RGFW_window_getMonitor(win); + RGFW_scaleUpdatedCallback(win, mon.scaleX, mon.scaleY); +} + +static BOOL RGFW__osxWantsUpdateLayer(id self, SEL _cmd) { RGFW_UNUSED(self); RGFW_UNUSED(_cmd); return YES; } + +static void RGFW__osxUpdateLayer(id self, SEL _cmd) { + RGFW_UNUSED(self); RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + RGFW_windowRefreshCallback(win); +} + +static void RGFW__osxDrawRect(id self, SEL _cmd, CGRect rect) { + RGFW_UNUSED(rect); RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_windowRefreshCallback(win); +} + +static void RGFW__osxMouseEntered(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + NSPoint p = ((NSPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow")); + RGFW_mouseNotifyCallback(win, (i32)p.x, (i32)(win->h - p.y), 1); +} + +static void RGFW__osxMouseExited(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); RGFW_UNUSED(event); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_mouseNotifyCallback(win, win->internal.lastMouseX, win->internal.lastMouseY, RGFW_FALSE); +} + +static void RGFW__osxKeyDown(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL || !(win->internal.enabledEvents & RGFW_keyPressedFlag)) return; + + u32 key = (u16)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode")); + u32 mappedKey = (u32)*(((char*)(const char*)NSString_to_char(((id(*)(id, SEL))objc_msgSend)(event, sel_registerName("charactersIgnoringModifiers"))))); + if ((u8)mappedKey == 239) mappedKey = 0; + + RGFW_key value = (u8)RGFW_apiKeyToRGFW(key); + RGFW_bool repeat = RGFW_window_isKeyPressed(win, value); + + RGFW_keyCallback(win, value, (u8)mappedKey, win->internal.mod, repeat, 1); +} + +static void RGFW__osxKeyUp(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL || !(win->internal.enabledEvents & RGFW_keyReleasedFlag)) return; + + u32 key = (u16)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("keyCode")); + u32 mappedKey = (u32)*(((char*)(const char*)NSString_to_char(((id(*)(id, SEL))objc_msgSend)(event, sel_registerName("charactersIgnoringModifiers"))))); + if ((u8)mappedKey == 239) mappedKey = 0; + + RGFW_key value = (u8)RGFW_apiKeyToRGFW(key); + RGFW_bool repeat = RGFW_window_isKeyDown(win, (u8)value); + + RGFW_keyCallback(win, value, (u8)mappedKey, win->internal.mod, repeat, 1); +} + +static void RGFW__osxFlagsChanged(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + RGFW_key value = 0; + RGFW_bool pressed = RGFW_FALSE; + + u32 flags = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("modifierFlags")); + RGFW_updateKeyModsEx(win, + ((u32)(flags & NSEventModifierFlagCapsLock) % 255), + ((flags & NSEventModifierFlagNumericPad) % 255), + ((flags & NSEventModifierFlagControl) % 255), + ((flags & NSEventModifierFlagOption) % 255), + ((flags & NSEventModifierFlagShift) % 255), + ((flags & NSEventModifierFlagCommand) % 255), 0); + u8 i; + for (i = 0; i < 9; i++) + _RGFW->keyboard[i + RGFW_capsLock].prev = _RGFW->keyboard[i + RGFW_capsLock].current; + + for (i = 0; i < 5; i++) { + u32 shift = (1 << (i + 16)); + u32 key = i + RGFW_capsLock; + if ((flags & shift) && !RGFW_window_isKeyDown(win, (u8)key)) { + pressed = RGFW_TRUE; + value = (u8)key; + break; + } + if (!(flags & shift) && RGFW_window_isKeyDown(win, (u8)key)) { + pressed = RGFW_FALSE; + value = (u8)key; + break; + } + } + + RGFW_bool repeat = RGFW_window_isKeyDown(win, (u8)value); + RGFW_keyCallback(win, value, 0, win->internal.mod, repeat, pressed); + + if (value != RGFW_capsLock) { + RGFW_keyCallback(win, value + 4, 0, win->internal.mod, repeat, pressed); + } + +} + +static void RGFW__osxMouseMoved(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + NSPoint p = ((NSPoint(*)(id, SEL))objc_msgSend)(event, sel_registerName("locationInWindow")); + + CGFloat vecX = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaX")); + CGFloat vecY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaY")); + + RGFW_mousePosCallback(win, (i32)p.x, (i32)(win->h - p.y), (float)vecX, (float)vecY); +} + +static void RGFW__osxMouseDown(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + u32 buttonNumber = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber")); + + RGFW_mouseButton value = 0; + switch (buttonNumber) { + case 0: value = RGFW_mouseLeft; break; + case 1: value = RGFW_mouseRight; break; + case 2: value = RGFW_mouseMiddle; break; + default: value = (u8)buttonNumber; + } + + RGFW_mouseButtonCallback(win, value, 1); +} + +static void RGFW__osxMouseUp(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + u32 buttonNumber = (u32)((u32(*)(id, SEL))objc_msgSend)(event, sel_registerName("buttonNumber")); + + RGFW_mouseButton value = 0; + switch (buttonNumber) { + case 0: value = RGFW_mouseLeft; break; + case 1: value = RGFW_mouseRight; break; + case 2: value = RGFW_mouseMiddle; break; + default: value = (u8)buttonNumber; + } + + RGFW_mouseButtonCallback(win, value, 0); +} + +static void RGFW__osxScrollWheel(id self, SEL _cmd, id event) { + RGFW_UNUSED(_cmd); + RGFW_window* win = NULL; + object_getInstanceVariable(self, "RGFW_window", (void**)&win); + if (win == NULL) return; + + float deltaX = (float)((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaX")); + float deltaY = (float)((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(event, sel_registerName("deltaY")); + + RGFW_mouseScrollCallback(win, deltaX, deltaY); +} + +RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + surface->native.format = RGFW_formatRGBA8; + + surface->native.buffer = (u8*)RGFW_ALLOC((size_t)(w * h * 4)); + return RGFW_TRUE; +} + +void RGFW_surface_freePtr(RGFW_surface* surface) { RGFW_FREE(surface->native.buffer); } + +void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + int minX = RGFW_MIN(win->w, surface->w); + int minY = RGFW_MIN(win->h, surface->h); + + RGFW_monitor mon = RGFW_window_getMonitor(win); + minX = (i32)((float)minX * mon.pixelRatio); + minY = (i32)((float)minY * mon.pixelRatio); + + RGFW_copyImageData(surface->native.buffer, surface->w, minY, surface->native.format, surface->data, surface->format); + + id image = ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSImage"), sel_getUid("alloc")); + NSSize size = (NSSize){(double)surface->w, (double)surface->h}; + image = ((id (*)(id, SEL, NSSize))objc_msgSend)((id)image, sel_getUid("initWithSize:"), size); + + id rep = NSBitmapImageRep_initWithBitmapData(&surface->native.buffer, minX, minY, 8, 4, true, false, "NSDeviceRGBColorSpace", 1 << 1, (u32)surface->w * 4, 32); + RGFW_copyImageData(NSBitmapImageRep_bitmapData(rep), minX, minY , RGFW_formatRGBA8, surface->native.buffer, surface->native.format); + ((void (*)(id, SEL, id))objc_msgSend)((id)image, sel_getUid("addRepresentation:"), rep); + + id contentView = ((id (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_getUid("contentView")); + ((void (*)(id, SEL, BOOL))objc_msgSend)(contentView, sel_getUid("setWantsLayer:"), YES); + id layer = ((id (*)(id, SEL))objc_msgSend)(contentView, sel_getUid("layer")); + + ((void (*)(id, SEL, id))objc_msgSend)(layer, sel_getUid("setContents:"), (id)image); + ((void (*)(id, SEL, BOOL))objc_msgSend)(contentView, sel_getUid("setNeedsDisplay:"), YES); + + NSRelease(rep); + NSRelease(image); + + objc_msgSend_bool_void(pool, sel_registerName("drain")); +} + +void* RGFW_window_getView_OSX(RGFW_window* win) { return win->src.view; } + +void RGFW_window_setLayer_OSX(RGFW_window* win, void* layer) { + objc_msgSend_void_id((id)win->src.view, sel_registerName("setLayer"), (id)layer); +} + +void* RGFW_getLayer_OSX(void) { + return objc_msgSend_class((id)objc_getClass("CAMetalLayer"), (SEL)sel_registerName("layer")); +} +void* RGFW_window_getWindow_OSX(RGFW_window* win) { return win->src.window; } + +void RGFW_initKeycodesPlatform(void) { + _RGFW->keycodes[0x1D] = RGFW_0; + _RGFW->keycodes[0x12] = RGFW_1; + _RGFW->keycodes[0x13] = RGFW_2; + _RGFW->keycodes[0x14] = RGFW_3; + _RGFW->keycodes[0x15] = RGFW_4; + _RGFW->keycodes[0x17] = RGFW_5; + _RGFW->keycodes[0x16] = RGFW_6; + _RGFW->keycodes[0x1A] = RGFW_7; + _RGFW->keycodes[0x1C] = RGFW_8; + _RGFW->keycodes[0x19] = RGFW_9; + _RGFW->keycodes[0x00] = RGFW_a; + _RGFW->keycodes[0x0B] = RGFW_b; + _RGFW->keycodes[0x08] = RGFW_c; + _RGFW->keycodes[0x02] = RGFW_d; + _RGFW->keycodes[0x0E] = RGFW_e; + _RGFW->keycodes[0x03] = RGFW_f; + _RGFW->keycodes[0x05] = RGFW_g; + _RGFW->keycodes[0x04] = RGFW_h; + _RGFW->keycodes[0x22] = RGFW_i; + _RGFW->keycodes[0x26] = RGFW_j; + _RGFW->keycodes[0x28] = RGFW_k; + _RGFW->keycodes[0x25] = RGFW_l; + _RGFW->keycodes[0x2E] = RGFW_m; + _RGFW->keycodes[0x2D] = RGFW_n; + _RGFW->keycodes[0x1F] = RGFW_o; + _RGFW->keycodes[0x23] = RGFW_p; + _RGFW->keycodes[0x0C] = RGFW_q; + _RGFW->keycodes[0x0F] = RGFW_r; + _RGFW->keycodes[0x01] = RGFW_s; + _RGFW->keycodes[0x11] = RGFW_t; + _RGFW->keycodes[0x20] = RGFW_u; + _RGFW->keycodes[0x09] = RGFW_v; + _RGFW->keycodes[0x0D] = RGFW_w; + _RGFW->keycodes[0x07] = RGFW_x; + _RGFW->keycodes[0x10] = RGFW_y; + _RGFW->keycodes[0x06] = RGFW_z; + _RGFW->keycodes[0x27] = RGFW_apostrophe; + _RGFW->keycodes[0x2A] = RGFW_backSlash; + _RGFW->keycodes[0x2B] = RGFW_comma; + _RGFW->keycodes[0x18] = RGFW_equals; + _RGFW->keycodes[0x32] = RGFW_backtick; + _RGFW->keycodes[0x21] = RGFW_bracket; + _RGFW->keycodes[0x1B] = RGFW_minus; + _RGFW->keycodes[0x2F] = RGFW_period; + _RGFW->keycodes[0x1E] = RGFW_closeBracket; + _RGFW->keycodes[0x29] = RGFW_semicolon; + _RGFW->keycodes[0x2C] = RGFW_slash; + _RGFW->keycodes[0x0A] = RGFW_world1; + _RGFW->keycodes[0x33] = RGFW_backSpace; + _RGFW->keycodes[0x39] = RGFW_capsLock; + _RGFW->keycodes[0x75] = RGFW_delete; + _RGFW->keycodes[0x7D] = RGFW_down; + _RGFW->keycodes[0x77] = RGFW_end; + _RGFW->keycodes[0x24] = RGFW_enter; + _RGFW->keycodes[0x35] = RGFW_escape; + _RGFW->keycodes[0x7A] = RGFW_F1; + _RGFW->keycodes[0x78] = RGFW_F2; + _RGFW->keycodes[0x63] = RGFW_F3; + _RGFW->keycodes[0x76] = RGFW_F4; + _RGFW->keycodes[0x60] = RGFW_F5; + _RGFW->keycodes[0x61] = RGFW_F6; + _RGFW->keycodes[0x62] = RGFW_F7; + _RGFW->keycodes[0x64] = RGFW_F8; + _RGFW->keycodes[0x65] = RGFW_F9; + _RGFW->keycodes[0x6D] = RGFW_F10; + _RGFW->keycodes[0x67] = RGFW_F11; + _RGFW->keycodes[0x6F] = RGFW_F12; + _RGFW->keycodes[0x69] = RGFW_printScreen; + _RGFW->keycodes[0x6B] = RGFW_F14; + _RGFW->keycodes[0x71] = RGFW_F15; + _RGFW->keycodes[0x6A] = RGFW_F16; + _RGFW->keycodes[0x40] = RGFW_F17; + _RGFW->keycodes[0x4F] = RGFW_F18; + _RGFW->keycodes[0x50] = RGFW_F19; + _RGFW->keycodes[0x5A] = RGFW_F20; + _RGFW->keycodes[0x73] = RGFW_home; + _RGFW->keycodes[0x72] = RGFW_insert; + _RGFW->keycodes[0x7B] = RGFW_left; + _RGFW->keycodes[0x3A] = RGFW_altL; + _RGFW->keycodes[0x3B] = RGFW_controlL; + _RGFW->keycodes[0x38] = RGFW_shiftL; + _RGFW->keycodes[0x37] = RGFW_superL; + _RGFW->keycodes[0x6E] = RGFW_menu; + _RGFW->keycodes[0x47] = RGFW_numLock; + _RGFW->keycodes[0x79] = RGFW_pageDown; + _RGFW->keycodes[0x74] = RGFW_pageUp; + _RGFW->keycodes[0x7C] = RGFW_right; + _RGFW->keycodes[0x3D] = RGFW_altR; + _RGFW->keycodes[0x3E] = RGFW_controlR; + _RGFW->keycodes[0x3C] = RGFW_shiftR; + _RGFW->keycodes[0x36] = RGFW_superR; + _RGFW->keycodes[0x31] = RGFW_space; + _RGFW->keycodes[0x30] = RGFW_tab; + _RGFW->keycodes[0x7E] = RGFW_up; + _RGFW->keycodes[0x52] = RGFW_kp0; + _RGFW->keycodes[0x53] = RGFW_kp1; + _RGFW->keycodes[0x54] = RGFW_kp2; + _RGFW->keycodes[0x55] = RGFW_kp3; + _RGFW->keycodes[0x56] = RGFW_kp4; + _RGFW->keycodes[0x57] = RGFW_kp5; + _RGFW->keycodes[0x58] = RGFW_kp6; + _RGFW->keycodes[0x59] = RGFW_kp7; + _RGFW->keycodes[0x5B] = RGFW_kp8; + _RGFW->keycodes[0x5C] = RGFW_kp9; + _RGFW->keycodes[0x45] = RGFW_kpSlash; + _RGFW->keycodes[0x41] = RGFW_kpPeriod; + _RGFW->keycodes[0x4B] = RGFW_kpSlash; + _RGFW->keycodes[0x4C] = RGFW_kpReturn; + _RGFW->keycodes[0x51] = RGFW_kpEqual; + _RGFW->keycodes[0x43] = RGFW_kpMultiply; + _RGFW->keycodes[0x4E] = RGFW_kpMinus; +} + +i32 RGFW_initPlatform(void) { + class_addMethod(objc_getClass("NSObject"), sel_registerName("windowShouldClose:"), (IMP)(void*)RGFW_OnClose, 0); + + /* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */ + class_addMethod(objc_getClass("NSWindowClass"), sel_registerName("acceptsFirstResponder:"), (IMP)(void*)RGFW__osxAcceptsFirstResponder, 0); + class_addMethod(objc_getClass("NSWindowClass"), sel_registerName("performKeyEquivalent:"), (IMP)(void*)RGFW__osxPerformKeyEquivalent, 0); + + _RGFW->NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication")); + + ((void (*)(id, SEL, NSUInteger))objc_msgSend) + ((id)_RGFW->NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular); + + _RGFW->customViewClasses[0] = objc_allocateClassPair(objc_getClass("NSView"), "RGFWCustomView", 0); + _RGFW->customViewClasses[1] = objc_allocateClassPair(objc_getClass("NSOpenGLView"), "RGFWOpenGLCustomView", 0); + for (size_t i = 0; i < 2; i++) { + class_addIvar((Class)_RGFW->customViewClasses[i], "RGFW_window", sizeof(RGFW_window*), sizeof(RGFW_window*), "L"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("drawRect:"), (IMP)RGFW__osxDrawRect, "v@:{CGRect=ffff}"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("viewDidChangeBackingProperties"), (IMP)RGFW__osxViewDidChangeBackingProperties, "v@:"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseDown:"), (IMP)RGFW__osxMouseDown, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseUp:"), (IMP)RGFW__osxMouseUp, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("scrollWheel:"), (IMP)RGFW__osxScrollWheel, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("rightMouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("otherMouseDragged:"), (IMP)RGFW__osxMouseMoved, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("keyDown:"), (IMP)RGFW__osxKeyDown, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("keyUp:"), (IMP)RGFW__osxKeyUp, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseMoved:"), (IMP)RGFW__osxMouseMoved, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseEntered:"), (IMP)RGFW__osxMouseEntered, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("mouseExited:"), (IMP)RGFW__osxMouseExited, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("flagsChanged:"), (IMP)RGFW__osxFlagsChanged, "v@:@"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_getUid("acceptsFirstResponder"), (IMP)RGFW__osxAcceptsFirstResponder, "B@:"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("initWithRGFWWindow:"), (IMP)RGFW__osxCustomInitWithRGFWWindow, "@@:{CGRect={CGPoint=dd}{CGSize=dd}}"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("wantsUpdateLayer"), (IMP)RGFW__osxWantsUpdateLayer, "B@:"); + class_addMethod((Class)_RGFW->customViewClasses[i], sel_registerName("updateLayer"), (IMP)RGFW__osxUpdateLayer, "v@:"); + objc_registerClassPair((Class)_RGFW->customViewClasses[i]); + } + _RGFW->customWindowDelegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "RGFWWindowDelegate", 0); + class_addIvar((Class)_RGFW->customWindowDelegateClass, "RGFW_window", sizeof(RGFW_window*), sizeof(RGFW_window*), "L"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidResize:"), (IMP)RGFW__osxDidWindowResize, "v@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidMiniaturize:"), (IMP) RGFW__osxWindowMiniaturize, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidDeminiaturize:"), (IMP) RGFW__osxWindowDeminiaturize, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidBecomeKey:"), (IMP) RGFW__osxWindowBecameKey, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("windowDidResignKey:"), (IMP) RGFW__osxWindowResignKey, ""); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingEntered:"), (IMP)RGFW__osxDraggingEntered, "l@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingUpdated:"), (IMP)RGFW__osxDraggingUpdated, "l@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("prepareForDragOperation:"), (IMP)RGFW__osxPrepareForDragOperation, "B@:@"); + class_addMethod((Class)_RGFW->customWindowDelegateClass, sel_registerName("performDragOperation:"), (IMP)RGFW__osxPerformDragOperation, "B@:@"); + objc_registerClassPair((Class)_RGFW->customWindowDelegateClass); + return 0; +} + +void RGFW_osx_initView(RGFW_window* win) { + NSRect contentRect; + contentRect.origin.x = 0; + contentRect.origin.y = 0; + contentRect.size.width = (double)win->w; + contentRect.size.height = (double)win->h; + ((void(*)(id, SEL, CGRect))objc_msgSend)((id)win->src.view, sel_registerName("setFrame:"), contentRect); + + + if (RGFW_COCOA_FRAME_NAME) + objc_msgSend_ptr(win->src.view, sel_registerName("setFrameAutosaveName:"), RGFW_COCOA_FRAME_NAME); + + object_setInstanceVariable((id)win->src.view, "RGFW_window", win); + objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view); + objc_msgSend_void_bool(win->src.view, sel_registerName("setWantsLayer:"), true); + objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4); + + id trackingArea = objc_msgSend_id(objc_getClass("NSTrackingArea"), sel_registerName("alloc")); + trackingArea = ((id (*)(id, SEL, NSRect, NSUInteger, id, id))objc_msgSend)( + trackingArea, + sel_registerName("initWithRect:options:owner:userInfo:"), + contentRect, + NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways | NSTrackingInVisibleRect, + (id)win->src.view, + nil + ); + + ((void (*)(id, SEL, id))objc_msgSend)((id)win->src.view, sel_registerName("addTrackingArea:"), trackingArea); + ((void (*)(id, SEL))objc_msgSend)(trackingArea, sel_registerName("release")); +} + +RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { + /* RR Create an autorelease pool */ + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + RGFW_window_setMouseDefault(win); + + NSRect windowRect; + windowRect.origin.x = (double)win->x; + windowRect.origin.y = (double)win->y; + windowRect.size.width = (double)win->w; + windowRect.size.height = (double)win->h; + NSBackingStoreType macArgs = (NSBackingStoreType)(NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled); + + if (!(flags & RGFW_windowNoResize)) + macArgs = (NSBackingStoreType)(macArgs | (NSBackingStoreType)NSWindowStyleMaskResizable); + if (!(flags & RGFW_windowNoBorder)) + macArgs = (NSBackingStoreType)(macArgs | (NSBackingStoreType)NSWindowStyleMaskTitled); + { + void* nsclass = objc_getClass("NSWindow"); + SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:"); + + win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend) + (NSAlloc(nsclass), func, windowRect, (NSWindowStyleMask)macArgs, macArgs, false); + } + + id str = NSString_stringWithUTF8String(name); + objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str); + + id delegate = objc_msgSend_id(NSAlloc((Class)_RGFW->customWindowDelegateClass), sel_registerName("init")); + object_setInstanceVariable(delegate, "RGFW_window", win); + + objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), delegate); + + if (flags & RGFW_windowAllowDND) { + win->internal.flags |= RGFW_windowAllowDND; + + NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString}; + NSregisterForDraggedTypes((id)win->src.window, types, 3); + } + + objc_msgSend_void_bool((id)win->src.window, sel_registerName("setAcceptsMouseMovedEvents:"), true); + + if (flags & RGFW_windowTransparent) { + objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false); + + objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), + NSColor_colorWithSRGB(0, 0, 0, 0)); + } + + /* Show the window */ + objc_msgSend_void_bool((id)_RGFW->NSApp, sel_registerName("activateIgnoringOtherApps:"), true); + + if (_RGFW->root == NULL) { + objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow")); + } + + objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow")); + + objc_msgSend_void((id)_RGFW->NSApp, sel_registerName("finishLaunching")); + NSRetain(win->src.window); + NSRetain(_RGFW->NSApp); + + win->src.view = ((id(*)(id, SEL, RGFW_window*))objc_msgSend) (NSAlloc((Class)_RGFW->customViewClasses[0]), sel_registerName("initWithRGFWWindow:"), win); + return win; +} + +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { + NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame")); + NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame")); + double offset = 0; + + RGFW_setBit(&win->internal.flags, RGFW_windowNoBorder, !border); + NSBackingStoreType storeType = (NSBackingStoreType)(NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView); + if (border) + storeType = (NSBackingStoreType)(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable); + if (!(win->internal.flags & RGFW_windowNoResize)) { + storeType = (NSBackingStoreType)(storeType | (NSBackingStoreType)NSWindowStyleMaskResizable); + } + + ((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)((id)win->src.window, sel_registerName("setStyleMask:"), storeType); + + if (!border) { + id miniaturizeButton = objc_msgSend_int((id)win->src.window, sel_registerName("standardWindowButton:"), NSWindowMiniaturizeButton); + id titleBarView = objc_msgSend_id(miniaturizeButton, sel_registerName("superview")); + objc_msgSend_void_bool(titleBarView, sel_registerName("setHidden:"), true); + + offset = (double)(frame.size.height - content.size.height); + } + + RGFW_window_resize(win, win->w, win->h + (i32)offset); + win->h -= (i32)offset; +} + +RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) { + RGFW_ASSERT(_RGFW->root != NULL); + + CGEventRef e = CGEventCreate(NULL); + CGPoint point = CGEventGetLocation(e); + CFRelease(e); + + if (x) *x = (i32)point.x; + if (y) *y = (i32)point.y; + return RGFW_TRUE; +} + +void RGFW_stopCheckEvents(void) { + id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); + + id e = (id) ((id(*)(Class, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend) + (objc_getClass("NSEvent"), sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"), + NSEventTypeApplicationDefined, (NSPoint){0, 0}, (NSEventModifierFlags)0, NULL, (NSInteger)0, NULL, 0, 0, 0); + + ((void (*)(id, SEL, id, bool))objc_msgSend) + ((id)_RGFW->NSApp, sel_registerName("postEvent:atStart:"), e, 1); + + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); +} + +void RGFW_waitForEvent(i32 waitMS) { + id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); + + void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend) + (objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS); + + SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"); + id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend) + ((id)_RGFW->NSApp, eventFunc, + ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); + + if (e) { + ((void (*)(id, SEL, id, bool))objc_msgSend) + ((id)_RGFW->NSApp, sel_registerName("postEvent:atStart:"), e, 1); + } + + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); +} + +u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) { + return (u8)rgfw_keycode; /* TODO */ +} + +void RGFW_pollEvents(void) { + /* + * TODO look to see if all these events can be replaced with callbacks + * callbacks seem to give better info on mac's api + */ + + RGFW_resetPrevState(); + + id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + eventPool = objc_msgSend_id(eventPool, sel_registerName("init")); + SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"); + + while (1) { + void* date = NULL; + id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend) + ((id)_RGFW->NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true); + + if (e == NULL) { + objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("sendEvent:"), e); + ((void(*)(id, SEL))objc_msgSend)((id)_RGFW->NSApp, sel_registerName("updateWindows")); + break; + } + + objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("sendEvent:"), e); + ((void(*)(id, SEL))objc_msgSend)((id)_RGFW->NSApp, sel_registerName("updateWindows")); + } + + objc_msgSend_bool_void(eventPool, sel_registerName("drain")); +} + + +void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { + RGFW_ASSERT(win != NULL); + + win->x = x; + win->y = y; + ((void(*)(id,SEL,NSPoint))objc_msgSend)((id)win->src.window, sel_registerName("setFrameOrigin:"), (NSPoint){(double)x, (double)y}); +} + +void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { + RGFW_ASSERT(win != NULL); + + NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame")); + NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame")); + float offset = (float)(frame.size.height - content.size.height); + + win->w = w; + win->h = h; + + + ((void(*)(id, SEL, CGRect))objc_msgSend)((id)win->src.view, sel_registerName("setFrame:"), (NSRect){{0, 0}, {(double)win->w, (double)win->h}}); + ((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend) + ((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{(double)win->x, (double)win->y}, {(double)win->w, (double)win->h + (double)offset}}, true, true); +} + +void RGFW_window_focus(RGFW_window* win) { + RGFW_ASSERT(win); + objc_msgSend_void_bool((id)_RGFW->NSApp, sel_registerName("activateIgnoringOtherApps:"), true); + ((void (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyWindow")); +} + +void RGFW_window_raise(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), (SEL)NULL); + objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey); +} + +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + if (fullscreen && (win->internal.flags & RGFW_windowFullscreen)) return; + if (!fullscreen && !(win->internal.flags & RGFW_windowFullscreen)) return; + + if (fullscreen) { + if (!(win->internal.flags & RGFW_windowFullscreen)) { + return; + } + + win->internal.oldX = win->x; + win->internal.oldY = win->y; + win->internal.oldW = win->w; + win->internal.oldH = win->h; + RGFW_monitor mon = RGFW_window_getMonitor(win); + win->x = mon.x; + win->y = mon.y; + win->w = mon.mode.w; + win->h = mon.mode.h; + win->internal.flags |= RGFW_windowFullscreen; + RGFW_window_resize(win, mon.mode.w, mon.mode.h); + RGFW_window_move(win, mon.x, mon.y); + } + objc_msgSend_void_SEL(win->src.window, sel_registerName("toggleFullScreen:"), NULL); + + if (!fullscreen) { + win->x = win->internal.oldX; + win->y = win->internal.oldY; + win->w = win->internal.oldW; + win->h = win->internal.oldH; + win->internal.flags &= ~(u32)RGFW_windowFullscreen; + + RGFW_window_resize(win, win->w, win->h); + RGFW_window_move(win, win->x, win->y); + } +} + +void RGFW_window_maximize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + if (RGFW_window_isMaximized(win)) return; + + win->internal.flags |= RGFW_windowMaximize; + objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL); +} + +void RGFW_window_minimize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL); +} + +void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { + RGFW_ASSERT(win != NULL); + if (floating) objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGFloatingWindowLevelKey); + else objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey); +} + +void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { + objc_msgSend_int(win->src.window, sel_registerName("setAlphaValue:"), opacity); + objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), (opacity < (u8)255)); + + if (opacity) + objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), NSColor_colorWithSRGB(0, 0, 0, opacity)); + +} + +void RGFW_window_restore(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + if (RGFW_window_isMaximized(win)) + objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL); + + objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL); + RGFW_window_show(win); +} + +RGFW_bool RGFW_window_isFloating(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + int level = ((int (*)(id, SEL))objc_msgSend) ((id)(win->src.window), (SEL)sel_registerName("level")); + return level > kCGNormalWindowLevelKey; +} + +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_ASSERT(win != NULL); + if (name == NULL) name = "\0"; + + id str = NSString_stringWithUTF8String(name); + objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str); +} + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { + objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough); +} +#endif + +void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { + if (w == 0 && h == 0) { w = 1; h = 1; }; + + ((void (*)(id, SEL, NSSize))objc_msgSend) + ((id)win->src.window, sel_registerName("setContentAspectRatio:"), (NSSize){(CGFloat)w, (CGFloat)h}); +} + +void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { + ((void (*)(id, SEL, NSSize))objc_msgSend) ((id)win->src.window, sel_registerName("setMinSize:"), (NSSize){(CGFloat)w, (CGFloat)h}); +} + +void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { + if (w == 0 && h == 0) { + RGFW_monitor mon = RGFW_window_getMonitor(win); + w = mon.mode.w; + h = mon.mode.h; + } + + ((void (*)(id, SEL, NSSize))objc_msgSend) + ((id)win->src.window, sel_registerName("setMaxSize:"), (NSSize){(CGFloat)w, (CGFloat)h}); +} + +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { + RGFW_ASSERT(win != NULL); + RGFW_UNUSED(type); + + if (data == NULL) { + objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("setApplicationIconImage:"), NULL); + return RGFW_TRUE; + } + + id representation = NSBitmapImageRep_initWithBitmapData(NULL, w, h, 8, (NSInteger)4, true, false, "NSCalibratedRGBColorSpace", 1 << 1, w * 4, 32); + RGFW_copyImageData(NSBitmapImageRep_bitmapData(representation), w, h, RGFW_formatRGBA8, data, format); + + id dock_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){(CGFloat)w, (CGFloat)h})); + + objc_msgSend_void_id(dock_image, sel_registerName("addRepresentation:"), representation); + + objc_msgSend_void_id((id)_RGFW->NSApp, sel_registerName("setApplicationIconImage:"), dock_image); + + NSRelease(dock_image); + NSRelease(representation); + + return RGFW_TRUE; +} + +id NSCursor_arrowStr(const char* str); +id NSCursor_arrowStr(const char* str) { + void* nclass = objc_getClass("NSCursor"); + SEL func = sel_registerName(str); + return (id) objc_msgSend_id(nclass, func); +} + +RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) { + if (data == NULL) { + objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set")); + return NULL; + } + + id representation = (id)NSBitmapImageRep_initWithBitmapData(NULL, w, h, 8, (NSInteger)4, true, false, "NSCalibratedRGBColorSpace", 1 << 1, w * 4, 32); + RGFW_copyImageData(NSBitmapImageRep_bitmapData(representation), w, h, RGFW_formatRGBA8, data, format); + + id cursor_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){(CGFloat)w, (CGFloat)h})); + + objc_msgSend_void_id(cursor_image, sel_registerName("addRepresentation:"), representation); + + id cursor = (id) ((id(*)(id, SEL, id, NSPoint))objc_msgSend) + (NSAlloc(objc_getClass("NSCursor")), sel_registerName("initWithImage:hotSpot:"), cursor_image, (NSPoint){0.0, 0.0}); + + NSRelease(cursor_image); + NSRelease(representation); + + return (void*)cursor; +} + +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { + RGFW_ASSERT(win != NULL); RGFW_ASSERT(mouse); + CGDisplayShowCursor(kCGDirectMainDisplay); + objc_msgSend_void((id)mouse, sel_registerName("set")); + win->src.mouse = mouse; +} + +void RGFW_freeMouse(RGFW_mouse* mouse) { + RGFW_ASSERT(mouse); + NSRelease((id)mouse); +} + +RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} + +void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { + RGFW_window_showMouseFlags(win, show); + if (show) CGDisplayShowCursor(kCGDirectMainDisplay); + else CGDisplayHideCursor(kCGDirectMainDisplay); +} + +RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouse) { + const char* cursorSelectorStr; + switch (stdMouse) { + case RGFW_mouseNormal: cursorSelectorStr = "arrowCursor"; break; + case RGFW_mouseArrow: cursorSelectorStr = "arrowCursor"; break; + case RGFW_mouseIbeam: cursorSelectorStr = "IBeamCursor"; break; + case RGFW_mouseCrosshair: cursorSelectorStr = "crosshairCursor"; break; + case RGFW_mousePointingHand: cursorSelectorStr = "pointingHandCursor"; break; + case RGFW_mouseResizeEW: cursorSelectorStr = "resizeLeftRightCursor"; break; + case RGFW_mouseResizeE: cursorSelectorStr = "resizeLeftRightCursor"; break; + case RGFW_mouseResizeW: cursorSelectorStr = "resizeLeftRightCursor"; break; + case RGFW_mouseResizeNS: cursorSelectorStr = "resizeUpDownCursor"; break; + case RGFW_mouseResizeN: cursorSelectorStr = "resizeUpDownCursor"; break; + case RGFW_mouseResizeS: cursorSelectorStr = "resizeUpDownCursor"; break; + case RGFW_mouseResizeNWSE: cursorSelectorStr = "_windowResizeNorthWestSouthEastCursor"; break; + case RGFW_mouseResizeNW: cursorSelectorStr = "_windowResizeNorthWestSouthEastCursor"; break; + case RGFW_mouseResizeSE: cursorSelectorStr = "_windowResizeNorthWestSouthEastCursor"; break; + case RGFW_mouseResizeNESW: cursorSelectorStr = "_windowResizeNorthEastSouthWestCursor"; break; + case RGFW_mouseResizeNE: cursorSelectorStr = "_windowResizeNorthEastSouthWestCursor"; break; + case RGFW_mouseResizeSW: cursorSelectorStr = "_windowResizeNorthEastSouthWestCursor"; break; + case RGFW_mouseResizeAll: cursorSelectorStr = "openHandCursor"; break; + case RGFW_mouseNotAllowed: cursorSelectorStr = "operationNotAllowedCursor"; break; + case RGFW_mouseWait: cursorSelectorStr = "arrowCursor"; break; + case RGFW_mouseProgress: cursorSelectorStr = "arrowCursor"; break; + default: + return RGFW_FALSE; + } + + id mouse = NSCursor_arrowStr(cursorSelectorStr); + + if (mouse == NULL) + return RGFW_FALSE; + + RGFW_UNUSED(win); + CGDisplayShowCursor(kCGDirectMainDisplay); + objc_msgSend_void(mouse, sel_registerName("set")); + win->src.mouse = mouse; + + return RGFW_TRUE; +} + +void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); RGFW_UNUSED(state); +} + +void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); + CGAssociateMouseAndMouseCursorPosition(!(state == RGFW_TRUE)); +} + +void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { + RGFW_UNUSED(win); + + win->internal.lastMouseX = x - win->x; + win->internal.lastMouseY = y - win->y; + CGWarpMouseCursorPosition((CGPoint){(CGFloat)x, (CGFloat)y}); +} + + +void RGFW_window_hide(RGFW_window* win) { + objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false); +} + +void RGFW_window_show(RGFW_window* win) { + if (win->internal.flags & RGFW_windowFocusOnShow) + ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL); + + ((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), NULL); + objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true); +} + +void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { + if (RGFW_window_isInFocus(win) && request) { + return; + } + + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + if (_RGFW->flash) { + ((void (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("cancelUserAttentionRequest:"), _RGFW->flash); + } + + switch (request) { + case RGFW_flashBriefly: + _RGFW->flash = ((NSInteger (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("requestUserAttention:"), NSInformationalRequest); + break; + case RGFW_flashUntilFocused: + _RGFW->flash = ((NSInteger (*)(id, SEL, NSInteger))objc_msgSend) ((id)_RGFW->NSApp, sel_registerName("requestUserAttention:"), NSCriticalRequest); + break; + default: break; + } + + objc_msgSend_bool_void(pool, sel_registerName("drain")); +} + +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible")); + return visible == NO && !RGFW_window_isMinimized(win); +} + +RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES; +} + +RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_bool b = (RGFW_bool)objc_msgSend_bool(win->src.window, sel_registerName("isZoomed")); + return b; +} + +id RGFW_getNSScreenForDisplayID(CGDirectDisplayID display); +id RGFW_getNSScreenForDisplayID(CGDirectDisplayID display) { + Class NSScreenClass = objc_getClass("NSScreen"); + + id screens = objc_msgSend_id(NSScreenClass, sel_registerName("screens")); + + NSUInteger count = (NSUInteger)objc_msgSend_uint(screens, sel_registerName("count")); + NSUInteger i; + for (i = 0; i < count; i++) { + id screen = ((id (*)(id, SEL, int))objc_msgSend) (screens, sel_registerName("objectAtIndex:"), (int)i); + id description = objc_msgSend_id(screen, sel_registerName("deviceDescription")); + id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber"); + id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey); + + if ((CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue")) == display) { + return screen; + } + } + + return NULL; +} + +u32 RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode); +u32 RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) { + if (mode) { + u32 refreshRate = (u32)CGDisplayModeGetRefreshRate(mode); + if (refreshRate != 0) return refreshRate; + } + +#ifndef RGFW_NO_IOKIT + u32 res = RGFW_osx_getFallbackRefreshRate(display); + if (res != 0) return res; +#else + RGFW_UNUSED(display); +#endif + return 60; +} + +RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display, id screen); +RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display, id screen) { + RGFW_monitor monitor; + + const char name[] = "MacOS\0"; + RGFW_MEMCPY(monitor.name, name, 6); + + CGRect bounds = CGDisplayBounds(display); + monitor.x = (i32)bounds.origin.x; + monitor.y = (i32)bounds.origin.y; + monitor.mode.w = (i32) bounds.size.width; + monitor.mode.h = (i32) bounds.size.height; + + monitor.mode.red = 8; monitor.mode.green = 8; monitor.mode.blue = 8; + + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display); + monitor.mode.refreshRate = RGFW_osx_getRefreshRate(display, mode); + CFRelease(mode); + + CGSize screenSizeMM = CGDisplayScreenSize(display); + monitor.physW = (float)screenSizeMM.width / 25.4f; + monitor.physH = (float)screenSizeMM.height / 25.4f; + + float ppi_width = (monitor.mode.w/monitor.physW); + float ppi_height = (monitor.mode.h/monitor.physH); + + monitor.pixelRatio = (float)((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret) (screen, sel_registerName("backingScaleFactor")); + float dpi = 96.0f * monitor.pixelRatio; + + monitor.scaleX = ((i32)(((float) (ppi_width) / dpi) * 10.0f)) / 10.0f; + monitor.scaleY = ((i32)(((float) (ppi_height) / dpi) * 10.0f)) / 10.0f; + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, "monitor found"); + return monitor; +} + + +RGFW_monitor* RGFW_getMonitors(size_t* len) { + static CGDirectDisplayID displays[RGFW_MAX_MONITORS]; + u32 count; + + if (CGGetActiveDisplayList(RGFW_MAX_MONITORS, displays, &count) != kCGErrorSuccess) + return NULL; + + if (count > RGFW_MAX_MONITORS) count = RGFW_MAX_MONITORS; + + static RGFW_monitor monitors[RGFW_MAX_MONITORS]; + + u32 i; + for (i = 0; i < RGFW_MAX_MONITORS; i++) + monitors[i] = RGFW_NSCreateMonitor(displays[i], RGFW_getNSScreenForDisplayID(displays[i])); + + if (len != NULL) *len = count; + return monitors; +} + +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { + CGPoint point = { (CGFloat)mon.x, (CGFloat)mon.y }; + + CGDirectDisplayID display; + u32 displayCount = 0; + CGError err = CGGetDisplaysWithPoint(point, 1, &display, &displayCount); + if (err != kCGErrorSuccess || displayCount != 1) + return RGFW_FALSE; + + CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL); + + if (allModes == NULL) + return RGFW_FALSE; + + CFIndex i; + for (i = 0; i < CFArrayGetCount(allModes); i++) { + CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i); + + RGFW_monitorMode foundMode; + foundMode.w = (i32)CGDisplayModeGetWidth(cmode); + foundMode.h = (i32)CGDisplayModeGetHeight(cmode); + foundMode.refreshRate = RGFW_osx_getRefreshRate(display, cmode); + foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8; + + if (RGFW_monitorModeCompare(mode, foundMode, request)) { + if (CGDisplaySetDisplayMode(display, cmode, NULL) == kCGErrorSuccess) { + CFRelease(allModes); + return RGFW_TRUE; + } + break; + } + } + + CFRelease(allModes); + + return RGFW_FALSE; +} + +RGFW_monitor RGFW_getPrimaryMonitor(void) { + CGDirectDisplayID primary = CGMainDisplayID(); + return RGFW_NSCreateMonitor(primary, RGFW_getNSScreenForDisplayID(primary)); +} + +RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + id screen = objc_msgSend_id(win->src.window, sel_registerName("screen")); + id description = objc_msgSend_id(screen, sel_registerName("deviceDescription")); + id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber"); + id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey); + + CGDirectDisplayID display = (CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue")); + + return RGFW_NSCreateMonitor(display, screen); +} + +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { + size_t clip_len; + char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString, &clip_len); + if (clip == NULL) return -1; + + if (str != NULL) { + if (strCapacity < clip_len) + return 0; + + RGFW_MEMCPY(str, clip, clip_len); + + str[clip_len] = '\0'; + } + + return (RGFW_ssize_t)clip_len; +} + +void RGFW_writeClipboard(const char* text, u32 textLen) { + RGFW_UNUSED(textLen); + + NSPasteboardType array[] = { NSPasteboardTypeString, NULL }; + NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL); + + SEL func = sel_registerName("setString:forType:"); + ((bool (*)(id, SEL, id, id))objc_msgSend) + (NSPasteboard_generalPasteboard(), func, NSString_stringWithUTF8String(text), NSString_stringWithUTF8String((const char*)NSPasteboardTypeString)); +} + +#ifdef RGFW_OPENGL +void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param); +void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param) { + ((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend) + (context, sel_registerName("setValues:forParameter:"), vals, param); +} + + +/* MacOS OpenGL API spares us yet again (there are no extensions) */ +RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char * extension, size_t len) { RGFW_UNUSED(extension); RGFW_UNUSED(len); return RGFW_FALSE; } + +RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) { + static CFBundleRef RGFWnsglFramework = NULL; + if (RGFWnsglFramework == NULL) + RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + + CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII); + + RGFW_proc symbol = (RGFW_proc)CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName); + + CFRelease(symbolName); + + return symbol; +} + +RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { + win->src.ctx.native = ctx; + win->src.gfxType = RGFW_gfxNativeOpenGL; + + i32 attribs[40]; + size_t render_type_index = 0; + { + RGFW_attribStack stack; + RGFW_attribStack_init(&stack, attribs, 40); + + i32 colorBits = (i32)(hints->red + hints->green + hints->blue + hints->alpha) / 4; + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAColorSize, colorBits); + + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAlphaSize, hints->alpha); + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFADepthSize, hints->depth); + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAStencilSize, hints->stencil); + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAuxBuffers, hints->auxBuffers); + RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAClosestPolicy); + if (hints->samples) { + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASampleBuffers, 1); + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASamples, hints->samples); + } else RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFASampleBuffers, 0); + + if (hints->doubleBuffer) + RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFADoubleBuffer); + + #ifdef RGFW_COCOA_GRAPHICS_SWITCHING + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAllowOfflineRenderers, kCGLPFASupportsAutomaticGraphicsSwitching) + #endif + #if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + if (hints->stereo]) RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAStereo); + #endif + + /* macOS has the surface attribs and the OpenGL attribs connected for some reason maybe this is to give macOS more control to limit openGL/the OpenGL version? */ + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAOpenGLProfile, + (hints->major >= 4) ? NSOpenGLProfileVersion4_1Core : (hints->major >= 3) ? + NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy); + + if (hints->major <= 2) { + i32 accumSize = (i32)(hints->accumRed + hints->accumGreen + hints->accumBlue + hints->accumAlpha) / 4; + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFAAccumSize, accumSize); + } + + if (hints->renderer == RGFW_glSoftware) { + RGFW_attribStack_pushAttribs(&stack, NSOpenGLPFARendererID, kCGLRendererGenericFloatID); + } else { + RGFW_attribStack_pushAttrib(&stack, NSOpenGLPFAAccelerated); + } + render_type_index = stack.count - 1; + + RGFW_attribStack_pushAttribs(&stack, 0, 0); + } + + void* format = (void*) ((id(*)(id, SEL, const u32*))objc_msgSend) (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), (u32*)attribs); + if (format == NULL) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "Failed to load pixel format for OpenGL"); + + assert(render_type_index + 3 < (sizeof(attribs) / sizeof(attribs[0]))); + attribs[render_type_index] = NSOpenGLPFARendererID; + attribs[render_type_index + 1] = kCGLRendererGenericFloatID; + attribs[render_type_index + 3] = 0; + + format = (void*) ((id(*)(id, SEL, const u32*))objc_msgSend) (NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), (u32*)attribs); + if (format == NULL) + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenGLContext, "and loading software rendering OpenGL failed"); + else + RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, "Switching to software rendering"); + } + + /* the pixel format can be passed directly to OpenGL context creation to create a context + this is because the format also includes information about the OpenGL version (which may be a bad thing) */ + + if (win->src.view) + NSRelease(win->src.view); + win->src.view = (id) ((id(*)(id, SEL, NSRect, u32*))objc_msgSend) (NSAlloc(_RGFW->customViewClasses[1]), + sel_registerName("initWithFrame:pixelFormat:"), (NSRect){{0, 0}, {(double)win->w, (double)win->h}}, (u32*)format); + + id share = NULL; + if (hints->share) { + share = (id)hints->share->ctx; + } + + win->src.ctx.native->ctx = ((id (*)(id, SEL, id, id))objc_msgSend)(NSAlloc(objc_getClass("NSOpenGLContext")), + sel_registerName("initWithFormat:shareContext:"), + (id)format, share); + + objc_msgSend_void_id(win->src.view, sel_registerName("setOpenGLContext:"), win->src.ctx.native->ctx); + if (win->internal.flags & RGFW_windowTransparent) { + i32 opacity = 0; + #define NSOpenGLCPSurfaceOpacity 236 + NSOpenGLContext_setValues((id)win->src.ctx.native->ctx, &opacity, (NSOpenGLContextParameter)NSOpenGLCPSurfaceOpacity); + + } + + objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("makeCurrentContext")); + + objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view); + objc_msgSend_void_bool(win->src.view, sel_registerName("setWantsLayer:"), true); + objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"), 4); + + RGFW_window_swapInterval_OpenGL(win, 0); + + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized."); + return RGFW_TRUE; +} + +void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { + objc_msgSend_void(ctx->ctx, sel_registerName("release")); + win->src.ctx.native->ctx = NULL; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed."); +} + +void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { + if (win) RGFW_ASSERT(win->src.ctx.native); + if (win != NULL) + objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("makeCurrentContext")); + else + objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("clearCurrentContext")); +} +void* RGFW_getCurrentContext_OpenGL(void) { + return objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("currentContext")); +} + +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { + RGFW_ASSERT(win && win->src.ctx.native); + objc_msgSend_void(win->src.ctx.native->ctx, sel_registerName("flushBuffer")); +} +void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL && win->src.ctx.native != NULL); + NSOpenGLContext_setValues((id)win->src.ctx.native->ctx, &swapInterval, (NSOpenGLContextParameter)222); +} +#endif + +void RGFW_deinitPlatform(void) { } + +void RGFW_window_closePlatform(RGFW_window* win) { + NSRelease(win->src.view); +} + +#ifdef RGFW_VULKAN +VkResult RGFW_window_createSurface_Vulkan(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) { + RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance); + RGFW_ASSERT(surface != NULL); + + *surface = VK_NULL_HANDLE; + id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc")); + pool = objc_msgSend_id(pool, sel_registerName("init")); + + id nsView = (id)win->src.view; + if (!nsView) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errMetal, "NSView is NULL for macOS window"); + return -1; + } + + + id layer = ((id (*)(id, SEL))objc_msgSend)(nsView, sel_registerName("layer")); + + void* metalLayer = RGFW_getLayer_OSX(); + if (metalLayer == NULL) { + return -1; + } + ((void (*)(id, SEL, id))objc_msgSend)((id)nsView, sel_registerName("setLayer:"), metalLayer); + ((void (*)(id, SEL, BOOL))objc_msgSend)(nsView, sel_registerName("setWantsLayer:"), YES); + + VkResult result; +/* + VkMetalSurfaceCreateInfoEXT macos; + macos.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + macos.slayer = metalLayer; + RGFW_MEMSET(&macos, 0, sizeof(macos)); + result = vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface); +*/ + + VkMacOSSurfaceCreateInfoMVK macos; + RGFW_MEMSET(&macos, 0, sizeof(macos)); + macos.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; + macos.pView = nsView; + + result = vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface); + + objc_msgSend_bool_void(pool, sel_registerName("drain")); + + return result; +} +#endif + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + id* nsView = (id*)window->src.view; + if (!nsView) { + RGFW_sendDebugInfo(RGFW_typeError, RGFW_errMetal, "NSView is NULL for macOS window"); + return NULL; + } + + ((void (*)(id, SEL, BOOL))objc_msgSend)(nsView, sel_registerName("setWantsLayer:"), YES); + id layer = ((id (*)(id, SEL))objc_msgSend)(nsView, sel_registerName("layer")); + + void* metalLayer = RGFW_getLayer_OSX(); + if (metalLayer == NULL) { + return NULL; + } + ((void (*)(id, SEL, id))objc_msgSend)((id)nsView, sel_registerName("setLayer:"), metalLayer); + layer = metalLayer; + + WGPUSurfaceSourceMetalLayer fromMetal = {0}; + fromMetal.chain.sType = WGPUSType_SurfaceSourceMetalLayer; +#ifdef __OBJC__ + fromMetal.layer = (__bridge CAMetalLayer*)layer; /* Use __bridge for ARC compatibility if mixing C/Obj-C */ +#else + fromMetal.layer = layer; +#endif + + surfaceDesc.nextInChain = (WGPUChainedStruct*)&fromMetal.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#endif + +#endif /* RGFW_MACOS */ + +/* + End of MaOS defines +*/ + +/* + WASM defines +*/ + +#ifdef RGFW_WASM +EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + RGFW_windowResizedCallback(_RGFW->root, E->windowInnerWidth, E->windowInnerHeight); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + if (!(_RGFW->root->internal.enabledEvents & RGFW_windowResizedFlag)) return EM_TRUE; + + static u8 fullscreen = RGFW_FALSE; + static i32 originalW, originalH; + + if (fullscreen == RGFW_FALSE) { + originalW = _RGFW->root->w; + originalH = _RGFW->root->h; + } + + fullscreen = !fullscreen; + _RGFW->root->w = E->screenWidth; + _RGFW->root->h = E->screenHeight; + + EM_ASM("Module.canvas.focus();"); + + if (fullscreen == RGFW_FALSE) { + _RGFW->root->w = originalW; + _RGFW->root->h = originalH; + } else { + #if __EMSCRIPTEN_major__ >= 1 && __EMSCRIPTEN_minor__ >= 29 && __EMSCRIPTEN_tiny__ >= 0 + EmscriptenFullscreenStrategy FSStrat = {0}; + FSStrat.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; + FSStrat.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF; + FSStrat.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + emscripten_request_fullscreen_strategy("#canvas", 1, &FSStrat); + #else + emscripten_request_fullscreen("#canvas", 1); + #endif + } + + emscripten_set_canvas_element_size("#canvas", _RGFW->root->w, _RGFW->root->h); + RGFW_windowResizedCallback(_RGFW->root, _RGFW->root->w, _RGFW->root->h); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E); + + RGFW_focusCallback(_RGFW->root, 1); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E); + + RGFW_focusCallback(_RGFW->root, 0); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + RGFW_mousePosCallback(_RGFW->root, E->targetX, E->targetY, E->movementX, E->movementY); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + int button = E->button; + if (button > 2) + button += 2; + + RGFW_mouseButtonCallback(_RGFW->root, button, 1); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + int button = E->button; + if (button > 2) + button += 2; + + RGFW_mouseButtonCallback(_RGFW->root, button, 0); + return EM_TRUE; +} + +EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + RGFW_mouseScrollCallback(_RGFW->root, E->deltaX, E->deltaY); + + return EM_TRUE; +} + +EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + if (!(_RGFW->root->internal.enabledEvents & RGFW_mouseButtonPressedFlag)) return EM_TRUE; + + size_t i; + for (i = 0; i < (size_t)E->numTouches; i++) { + RGFW_mousePosCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY, 0, 0); + RGFW_mouseButtonCallback(_RGFW->root, RGFW_mouseLeft, 1); + } + + return EM_TRUE; +} + +EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + if (!(_RGFW->root->internal.enabledEvents & RGFW_mousePosChangedFlag)) return EM_TRUE; + + size_t i; + for (i = 0; i < (size_t)E->numTouches; i++) { + RGFW_mousePosCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY, 0, 0); + } + return EM_TRUE; +} + +EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* E, void* userData) { + RGFW_UNUSED(eventType); RGFW_UNUSED(userData); + + if (!(_RGFW->root->internal.enabledEvents & RGFW_mouseButtonReleasedFlag)) return EM_TRUE; + + size_t i; + for (i = 0; i < (size_t)E->numTouches; i++) { + RGFW_mousePosCallback(_RGFW->root, E->touches[i].targetX, E->touches[i].targetY, 0, 0); + RGFW_mouseButtonCallback(_RGFW->root, RGFW_mouseLeft, 0); + } + return EM_TRUE; +} + +EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* E, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); return EM_TRUE; } + +u32 RGFW_WASMPhysicalToRGFW(u32 hash); + +void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyEvent(char* key, char* code, RGFW_bool press) { + const char* iCode = code; + + u32 hash = 0; + while(*iCode) hash = ((hash ^ 0x7E057D79U) << 3) ^ (unsigned int)*iCode++; + + u32 physicalKey = RGFW_WASMPhysicalToRGFW(hash); + + u8 mappedKey = (u8)(*((u32*)key)); + + if (*((u16*)key) != mappedKey) { + mappedKey = 0; + if (*((u32*)key) == *((u32*)"Tab")) mappedKey = RGFW_tab; + } + + RGFW_keyCallback(_RGFW->root, physicalKey, mappedKey, _RGFW->root->internal.mod, RGFW_window_isKeyDown(_RGFW->root, (u8)physicalKey), press); +} + +void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyMods(RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) { + RGFW_updateKeyModsEx(_RGFW->root, capital, numlock, control, alt, shift, super, scroll); +} + +void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) { + RGFW_dataDropCallback(_RGFW->root, _RGFW->files, count); +} + +void RGFW_stopCheckEvents(void) { + _RGFW->stopCheckEvents_bool = RGFW_TRUE; +} + +RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { + surface->data = data; + surface->w = w; + surface->h = h; + surface->format = format; + return RGFW_TRUE; +} + +void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { + /* TODO: Needs fixing. */ + RGFW_copyImageData(surface->data, surface->w, RGFW_MIN(win->h, surface->h), RGFW_formatRGBA8, surface->data, surface->format); + EM_ASM_({ + var data = Module.HEAPU8.slice($0, $0 + $1 * $2 * 4); + let context = document.getElementById("canvas").getContext("2d"); + let image = context.getImageData(0, 0, $1, $2); + image.data.set(data); + context.putImageData(image, 0, $4 - $2); + }, surface->data, surface->w, surface->h, RGFW_MIN(win->h, surface->w), RGFW_MIN(win->h, surface->h)); +} + +void RGFW_surface_freePtr(RGFW_surface* surface) { } + +void EMSCRIPTEN_KEEPALIVE RGFW_makeSetValue(size_t index, char* file) { + /* This seems like a terrible idea, don't replicate this unless you hate yourself or the OS */ + /* TODO: find a better way to do this + */ + RGFW_STRNCPY((char*)_RGFW->files[index], file, RGFW_MAX_PATH - 1); + _RGFW->files[index][RGFW_MAX_PATH - 1] = '\0'; +} + +#include +#include +#include +#include + +void EMSCRIPTEN_KEEPALIVE RGFW_mkdir(char* name) { mkdir(name, 0755); } + +void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, size_t len) { + FILE* file = fopen(path, "w+"); + if (file == NULL) + return; + + fwrite(data, sizeof(char), len, file); + fclose(file); +} + +void RGFW_initKeycodesPlatform(void) { + _RGFW->keycodes[DOM_VK_BACK_QUOTE] = RGFW_backtick; + _RGFW->keycodes[DOM_VK_0] = RGFW_0; + _RGFW->keycodes[DOM_VK_1] = RGFW_1; + _RGFW->keycodes[DOM_VK_2] = RGFW_2; + _RGFW->keycodes[DOM_VK_3] = RGFW_3; + _RGFW->keycodes[DOM_VK_4] = RGFW_4; + _RGFW->keycodes[DOM_VK_5] = RGFW_5; + _RGFW->keycodes[DOM_VK_6] = RGFW_6; + _RGFW->keycodes[DOM_VK_7] = RGFW_7; + _RGFW->keycodes[DOM_VK_8] = RGFW_8; + _RGFW->keycodes[DOM_VK_9] = RGFW_9; + _RGFW->keycodes[DOM_VK_SPACE] = RGFW_space; + _RGFW->keycodes[DOM_VK_A] = RGFW_a; + _RGFW->keycodes[DOM_VK_B] = RGFW_b; + _RGFW->keycodes[DOM_VK_C] = RGFW_c; + _RGFW->keycodes[DOM_VK_D] = RGFW_d; + _RGFW->keycodes[DOM_VK_E] = RGFW_e; + _RGFW->keycodes[DOM_VK_F] = RGFW_f; + _RGFW->keycodes[DOM_VK_G] = RGFW_g; + _RGFW->keycodes[DOM_VK_H] = RGFW_h; + _RGFW->keycodes[DOM_VK_I] = RGFW_i; + _RGFW->keycodes[DOM_VK_J] = RGFW_j; + _RGFW->keycodes[DOM_VK_K] = RGFW_k; + _RGFW->keycodes[DOM_VK_L] = RGFW_l; + _RGFW->keycodes[DOM_VK_M] = RGFW_m; + _RGFW->keycodes[DOM_VK_N] = RGFW_n; + _RGFW->keycodes[DOM_VK_O] = RGFW_o; + _RGFW->keycodes[DOM_VK_P] = RGFW_p; + _RGFW->keycodes[DOM_VK_Q] = RGFW_q; + _RGFW->keycodes[DOM_VK_R] = RGFW_r; + _RGFW->keycodes[DOM_VK_S] = RGFW_s; + _RGFW->keycodes[DOM_VK_T] = RGFW_t; + _RGFW->keycodes[DOM_VK_U] = RGFW_u; + _RGFW->keycodes[DOM_VK_V] = RGFW_v; + _RGFW->keycodes[DOM_VK_W] = RGFW_w; + _RGFW->keycodes[DOM_VK_X] = RGFW_x; + _RGFW->keycodes[DOM_VK_Y] = RGFW_y; + _RGFW->keycodes[DOM_VK_Z] = RGFW_z; + _RGFW->keycodes[DOM_VK_PERIOD] = RGFW_period; + _RGFW->keycodes[DOM_VK_COMMA] = RGFW_comma; + _RGFW->keycodes[DOM_VK_SLASH] = RGFW_slash; + _RGFW->keycodes[DOM_VK_OPEN_BRACKET] = RGFW_bracket; + _RGFW->keycodes[DOM_VK_CLOSE_BRACKET] = RGFW_closeBracket; + _RGFW->keycodes[DOM_VK_SEMICOLON] = RGFW_semicolon; + _RGFW->keycodes[DOM_VK_QUOTE] = RGFW_apostrophe; + _RGFW->keycodes[DOM_VK_BACK_SLASH] = RGFW_backSlash; + _RGFW->keycodes[DOM_VK_RETURN] = RGFW_return; + _RGFW->keycodes[DOM_VK_DELETE] = RGFW_delete; + _RGFW->keycodes[DOM_VK_NUM_LOCK] = RGFW_numLock; + _RGFW->keycodes[DOM_VK_DIVIDE] = RGFW_kpSlash; + _RGFW->keycodes[DOM_VK_MULTIPLY] = RGFW_kpMultiply; + _RGFW->keycodes[DOM_VK_SUBTRACT] = RGFW_kpMinus; + _RGFW->keycodes[DOM_VK_NUMPAD1] = RGFW_kp1; + _RGFW->keycodes[DOM_VK_NUMPAD2] = RGFW_kp2; + _RGFW->keycodes[DOM_VK_NUMPAD3] = RGFW_kp3; + _RGFW->keycodes[DOM_VK_NUMPAD4] = RGFW_kp4; + _RGFW->keycodes[DOM_VK_NUMPAD5] = RGFW_kp5; + _RGFW->keycodes[DOM_VK_NUMPAD6] = RGFW_kp6; + _RGFW->keycodes[DOM_VK_NUMPAD9] = RGFW_kp9; + _RGFW->keycodes[DOM_VK_NUMPAD0] = RGFW_kp0; + _RGFW->keycodes[DOM_VK_DECIMAL] = RGFW_kpPeriod; + _RGFW->keycodes[DOM_VK_RETURN] = RGFW_kpReturn; + _RGFW->keycodes[DOM_VK_HYPHEN_MINUS] = RGFW_minus; + _RGFW->keycodes[DOM_VK_EQUALS] = RGFW_equals; + _RGFW->keycodes[DOM_VK_BACK_SPACE] = RGFW_backSpace; + _RGFW->keycodes[DOM_VK_TAB] = RGFW_tab; + _RGFW->keycodes[DOM_VK_CAPS_LOCK] = RGFW_capsLock; + _RGFW->keycodes[DOM_VK_SHIFT] = RGFW_shiftL; + _RGFW->keycodes[DOM_VK_CONTROL] = RGFW_controlL; + _RGFW->keycodes[DOM_VK_ALT] = RGFW_altL; + _RGFW->keycodes[DOM_VK_META] = RGFW_superL; + _RGFW->keycodes[DOM_VK_F1] = RGFW_F1; + _RGFW->keycodes[DOM_VK_F2] = RGFW_F2; + _RGFW->keycodes[DOM_VK_F3] = RGFW_F3; + _RGFW->keycodes[DOM_VK_F4] = RGFW_F4; + _RGFW->keycodes[DOM_VK_F5] = RGFW_F5; + _RGFW->keycodes[DOM_VK_F6] = RGFW_F6; + _RGFW->keycodes[DOM_VK_F7] = RGFW_F7; + _RGFW->keycodes[DOM_VK_F8] = RGFW_F8; + _RGFW->keycodes[DOM_VK_F9] = RGFW_F9; + _RGFW->keycodes[DOM_VK_F10] = RGFW_F10; + _RGFW->keycodes[DOM_VK_F11] = RGFW_F11; + _RGFW->keycodes[DOM_VK_F12] = RGFW_F12; + _RGFW->keycodes[DOM_VK_UP] = RGFW_up; + _RGFW->keycodes[DOM_VK_DOWN] = RGFW_down; + _RGFW->keycodes[DOM_VK_LEFT] = RGFW_left; + _RGFW->keycodes[DOM_VK_RIGHT] = RGFW_right; + _RGFW->keycodes[DOM_VK_INSERT] = RGFW_insert; + _RGFW->keycodes[DOM_VK_END] = RGFW_end; + _RGFW->keycodes[DOM_VK_PAGE_UP] = RGFW_pageUp; + _RGFW->keycodes[DOM_VK_PAGE_DOWN] = RGFW_pageDown; + _RGFW->keycodes[DOM_VK_ESCAPE] = RGFW_escape; + _RGFW->keycodes[DOM_VK_HOME] = RGFW_home; + _RGFW->keycodes[DOM_VK_SCROLL_LOCK] = RGFW_scrollLock; + _RGFW->keycodes[DOM_VK_PRINTSCREEN] = RGFW_printScreen; + _RGFW->keycodes[DOM_VK_PAUSE] = RGFW_pause; + _RGFW->keycodes[DOM_VK_F13] = RGFW_F13; + _RGFW->keycodes[DOM_VK_F14] = RGFW_F14; + _RGFW->keycodes[DOM_VK_F15] = RGFW_F15; + _RGFW->keycodes[DOM_VK_F16] = RGFW_F16; + _RGFW->keycodes[DOM_VK_F17] = RGFW_F17; + _RGFW->keycodes[DOM_VK_F18] = RGFW_F18; + _RGFW->keycodes[DOM_VK_F19] = RGFW_F19; + _RGFW->keycodes[DOM_VK_F20] = RGFW_F20; + _RGFW->keycodes[DOM_VK_F21] = RGFW_F21; + _RGFW->keycodes[DOM_VK_F22] = RGFW_F22; + _RGFW->keycodes[DOM_VK_F23] = RGFW_F23; + _RGFW->keycodes[DOM_VK_F24] = RGFW_F24; +} + +i32 RGFW_initPlatform(void) { return 0; } + +RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { + emscripten_set_canvas_element_size("#canvas", win->w, win->h); + emscripten_set_window_title(name); + + /* load callbacks */ + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_resize); + emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, Emscripten_on_fullscreenchange); + emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousemove); + emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchstart); + emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchend); + emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchmove); + emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchcancel); + emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousedown); + emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mouseup); + emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_wheel); + emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusin); + emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusout); + + if (flags & RGFW_windowAllowDND) { + win->internal.flags |= RGFW_windowAllowDND; + } + + EM_ASM({ + window.addEventListener("keydown", + (event) => { + var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code); + Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock")); + Module._RGFW_handleKeyEvent(key, code, 1); + _free(key); _free(code); + }, + true); + window.addEventListener("keyup", + (event) => { + var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code); + Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock")); + Module._RGFW_handleKeyEvent(key, code, 0); + _free(key); _free(code); + }, + true); + }); + + EM_ASM({ + var canvas = document.getElementById('canvas'); + canvas.addEventListener('drop', function(e) { + e.preventDefault(); + if (e.dataTransfer.file < 0) + return; + + var filenamesArray = []; + var count = e.dataTransfer.files.length; + + /* Read and save the files to emscripten's files */ + var drop_dir = '.rgfw_dropped_files'; + Module._RGFW_mkdir(drop_dir); + + for (var i = 0; i < count; i++) { + var file = e.dataTransfer.files[i]; + + var path = '/' + drop_dir + '/' + file.name.replace("//", '_'); + var reader = new FileReader(); + + reader.onloadend = (e) => { + if (reader.readyState != 2) { + out('failed to read dropped file: '+file.name+': '+reader.error); + } + else { + var data = e.target.result; + + Module._RGFW_writeFile(path, new Uint8Array(data), file.size); + } + }; + + reader.readAsArrayBuffer(file); + /* This works weird on modern OpenGL */ + var filename = stringToNewUTF8(path); + + filenamesArray.push(filename); + + Module._RGFW_makeSetValue(i, filename); + } + + Module._Emscripten_onDrop(count); + + for (var i = 0; i < count; ++i) { + _free(filenamesArray[i]); + } + }, true); + + canvas.addEventListener('dragover', function(e) { e.preventDefault(); return false; }, true); + }); + + return win; +} + +u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) { + return (u8)rgfw_keycode; /* TODO */ +} + +void RGFW_pollEvents(void) { + RGFW_resetPrevState(); + emscripten_sleep(0); +} + +void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { + RGFW_UNUSED(win); + emscripten_set_canvas_element_size("#canvas", w, h); +} + +/* NOTE: I don't know if this is possible */ +void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { RGFW_UNUSED(win); RGFW_UNUSED(x); RGFW_UNUSED(y); } +/* this one might be possible but it looks iffy */ +RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) { RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); return NULL; } + +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { RGFW_UNUSED(win); RGFW_UNUSED(mouse); } +void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_UNUSED(mouse); } + +RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { + RGFW_UNUSED(win); + char* cursorName = NULL; + + switch (mouse) { + case RGFW_mouseNormal: cursorName = (char*)"default"; break; + case RGFW_mouseArrow: cursorName = (char*)"default"; break; + case RGFW_mouseIbeam: cursorName = (char*)"text"; break; + case RGFW_mouseCrosshair: cursorName = (char*)"crosshair"; break; + case RGFW_mousePointingHand: cursorName = (char*)"pointer"; break; + case RGFW_mouseResizeEW: cursorName = (char*)"ew-resize"; break; + case RGFW_mouseResizeNS: cursorName = (char*)"ns-resize"; break; + case RGFW_mouseResizeNWSE: cursorName = (char*)"nwse-resize"; break; + case RGFW_mouseResizeNESW: cursorName = (char*)"nesw-resize"; break; + case RGFW_mouseResizeNW: cursorName = (char*)"nw-resize"; break; + case RGFW_mouseResizeN: cursorName = (char*)"n-resize"; break; + case RGFW_mouseResizeNE: cursorName = (char*)"ne-resize"; break; + case RGFW_mouseResizeE: cursorName = (char*)"e-resize"; break; + case RGFW_mouseResizeSE: cursorName = (char*)"se-resize"; break; + case RGFW_mouseResizeS: cursorName = (char*)"s-resize"; break; + case RGFW_mouseResizeSW: cursorName = (char*)"sw-resize"; break; + case RGFW_mouseResizeW: cursorName = (char*)"w-resize"; break; + case RGFW_mouseResizeAll: cursorName = (char*)"move"; break; + case RGFW_mouseNotAllowed: cursorName = (char*)"not-allowed"; break; + case RGFW_mouseWait: cursorName = (char*)"wait"; break; + case RGFW_mouseProgress: cursorName = (char*)"progress"; break; + default: return RGFW_FALSE; + } + + EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursorName); + return RGFW_TRUE; +} + +RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseNormal); +} + +void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) { + RGFW_window_showMouseFlags(win, show); + if (show) + RGFW_window_setMouseDefault(win); + else + EM_ASM(document.getElementById('canvas').style.cursor = 'none';); +} + +RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) { + if(x) *x = EM_ASM_INT({ + return window.mouseX || 0; + }); + if (y) *y = EM_ASM_INT({ + return window.mouseY || 0; + }); + return RGFW_TRUE; +} + +void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { + RGFW_UNUSED(win); + + EM_ASM_({ + var canvas = document.getElementById('canvas'); + if ($0) { + canvas.style.pointerEvents = 'none'; + } else { + canvas.style.pointerEvents = 'auto'; + } + }, passthrough); +} + +void RGFW_writeClipboard(const char* text, u32 textLen) { + RGFW_UNUSED(textLen); + EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text); +} + + +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { + RGFW_UNUSED(str); RGFW_UNUSED(strCapacity); + /* + placeholder code for later + I'm not sure if this is possible do the the async stuff + */ + return 0; +} + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { + win->src.ctx.native = ctx; + win->src.gfxType = RGFW_gfxNativeOpenGL; + + EmscriptenWebGLContextAttributes attrs; + attrs.alpha = hints->alpha; + attrs.depth = hints->depth; + attrs.stencil = hints->stencil; + attrs.antialias = hints->samples; + attrs.premultipliedAlpha = EM_TRUE; + attrs.preserveDrawingBuffer = EM_FALSE; + + if (hints->doubleBuffer == 0) + attrs.renderViaOffscreenBackBuffer = 0; + else + attrs.renderViaOffscreenBackBuffer = hints->auxBuffers; + + attrs.failIfMajorPerformanceCaveat = EM_FALSE; + attrs.majorVersion = (hints->major == 0) ? 1 : hints->major; + attrs.minorVersion = hints->minor; + + attrs.enableExtensionsByDefault = EM_TRUE; + attrs.explicitSwapControl = EM_TRUE; + + emscripten_webgl_init_context_attributes(&attrs); + win->src.ctx.native->ctx = emscripten_webgl_create_context("#canvas", &attrs); + emscripten_webgl_make_context_current(win->src.ctx.native->ctx); + + #ifdef LEGACY_GL_EMULATION + EM_ASM("Module.useWebGL = true; GLImmediate.init();"); + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context initalized."); + #endif + + RGFW_window_swapInterval_OpenGL(win, 0); + + return RGFW_TRUE; +} + +void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { + emscripten_webgl_destroy_context(ctx->ctx); + win->src.ctx.native->ctx = 0; + RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, "OpenGL context freed."); +} + +void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { + if (win) RGFW_ASSERT(win->src.ctx.native); + if (win == NULL) + emscripten_webgl_make_context_current(0); + else + emscripten_webgl_make_context_current(win->src.ctx.native->ctx); +} + +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { + RGFW_ASSERT(win && win->src.ctx.native); + emscripten_webgl_commit_frame(); +} +void* RGFW_getCurrentContext_OpenGL(void) { return (void*)emscripten_webgl_get_current_context(); } + +RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len) { + return EM_ASM_INT({ + var ext = UTF8ToString($0, $1); + var canvas = document.querySelector('canvas'); + var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + if (!gl) return 0; + + var supported = gl.getSupportedExtensions(); + return supported && supported.includes(ext) ? 1 : 0; + }, extension, len); + return RGFW_FALSE; +} + +RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) { + return (RGFW_proc)emscripten_webgl_get_proc_address(procname); + return NULL; +} + +#endif + +void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } + +void RGFW_deinitPlatform(void) { } + +void RGFW_window_closePlatform(RGFW_window* win) { } + +int RGFW_innerWidth(void) { return EM_ASM_INT({ return window.innerWidth; }); } +int RGFW_innerHeight(void) { return EM_ASM_INT({ return window.innerHeight; }); } + +void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); RGFW_UNUSED(state); +} + +void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { + RGFW_UNUSED(win); + if (state) { + emscripten_request_pointerlock("#canvas", 1); + } else { + emscripten_exit_pointerlock(); + } +} + +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_UNUSED(win); + if (name == NULL) name = "\0"; + + emscripten_set_window_title(name); +} + +void RGFW_window_maximize(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + + RGFW_monitor mon = RGFW_window_getMonitor(win); + RGFW_window_move(win, 0, 0); + RGFW_window_resize(win, mon.mode.w, mon.mode.h); +} + +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { + RGFW_ASSERT(win != NULL); + if (fullscreen) { + win->internal.flags |= RGFW_windowFullscreen; + EM_ASM( Module.requestFullscreen(false, true); ); + return; + } + win->internal.flags &= ~(u32)RGFW_windowFullscreen; + EM_ASM( Module.exitFullscreen(false, true); ); +} + +void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { + RGFW_UNUSED(win); + EM_ASM({ + var element = document.getElementById("canvas"); + if (element) + element.style.opacity = $1; + }, "elementId", opacity); +} + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { + WGPUSurfaceDescriptor surfaceDesc = {0}; + WGPUEmscriptenSurfaceSourceCanvasHTMLSelector canvasDesc = {0}; + canvasDesc.chain.sType = WGPUSType_EmscriptenSurfaceSourceCanvasHTMLSelector; + canvasDesc.selector = (WGPUStringView){.data = "#canvas", .length = 7}; + + surfaceDesc.nextInChain = &canvasDesc.chain; + return wgpuInstanceCreateSurface(instance, &surfaceDesc); +} +#endif + +u32 RGFW_WASMPhysicalToRGFW(u32 hash) { + switch(hash) { /* 0x0000 */ + case 0x67243A2DU /* Escape */: return RGFW_escape; /* 0x0001 */ + case 0x67251058U /* Digit0 */: return RGFW_0; /* 0x0002 */ + case 0x67251059U /* Digit1 */: return RGFW_1; /* 0x0003 */ + case 0x6725105AU /* Digit2 */: return RGFW_2; /* 0x0004 */ + case 0x6725105BU /* Digit3 */: return RGFW_3; /* 0x0005 */ + case 0x6725105CU /* Digit4 */: return RGFW_4; /* 0x0006 */ + case 0x6725105DU /* Digit5 */: return RGFW_5; /* 0x0007 */ + case 0x6725105EU /* Digit6 */: return RGFW_6; /* 0x0008 */ + case 0x6725105FU /* Digit7 */: return RGFW_7; /* 0x0009 */ + case 0x67251050U /* Digit8 */: return RGFW_8; /* 0x000A */ + case 0x67251051U /* Digit9 */: return RGFW_9; /* 0x000B */ + case 0x92E14DD3U /* Minus */: return RGFW_minus; /* 0x000C */ + case 0x92E1FBACU /* Equal */: return RGFW_equals; /* 0x000D */ + case 0x36BF1CB5U /* Backspace */: return RGFW_backSpace; /* 0x000E */ + case 0x7B8E51E2U /* Tab */: return RGFW_tab; /* 0x000F */ + case 0x2C595B51U /* KeyQ */: return RGFW_q; /* 0x0010 */ + case 0x2C595B57U /* KeyW */: return RGFW_w; /* 0x0011 */ + case 0x2C595B45U /* KeyE */: return RGFW_e; /* 0x0012 */ + case 0x2C595B52U /* KeyR */: return RGFW_r; /* 0x0013 */ + case 0x2C595B54U /* KeyT */: return RGFW_t; /* 0x0014 */ + case 0x2C595B59U /* KeyY */: return RGFW_y; /* 0x0015 */ + case 0x2C595B55U /* KeyU */: return RGFW_u; /* 0x0016 */ + case 0x2C595B4FU /* KeyO */: return RGFW_o; /* 0x0018 */ + case 0x2C595B50U /* KeyP */: return RGFW_p; /* 0x0019 */ + case 0x45D8158CU /* BracketLeft */: return RGFW_closeBracket; /* 0x001A */ + case 0xDEEABF7CU /* BracketRight */: return RGFW_bracket; /* 0x001B */ + case 0x92E1C5D2U /* Enter */: return RGFW_return; /* 0x001C */ + case 0xE058958CU /* ControlLeft */: return RGFW_controlL; /* 0x001D */ + case 0x2C595B41U /* KeyA */: return RGFW_a; /* 0x001E */ + case 0x2C595B53U /* KeyS */: return RGFW_s; /* 0x001F */ + case 0x2C595B44U /* KeyD */: return RGFW_d; /* 0x0020 */ + case 0x2C595B46U /* KeyF */: return RGFW_f; /* 0x0021 */ + case 0x2C595B47U /* KeyG */: return RGFW_g; /* 0x0022 */ + case 0x2C595B48U /* KeyH */: return RGFW_h; /* 0x0023 */ + case 0x2C595B4AU /* KeyJ */: return RGFW_j; /* 0x0024 */ + case 0x2C595B4BU /* KeyK */: return RGFW_k; /* 0x0025 */ + case 0x2C595B4CU /* KeyL */: return RGFW_l; /* 0x0026 */ + case 0x2707219EU /* Semicolon */: return RGFW_semicolon; /* 0x0027 */ + case 0x92E0B58DU /* Quote */: return RGFW_apostrophe; /* 0x0028 */ + case 0x36BF358DU /* Backquote */: return RGFW_backtick; /* 0x0029 */ + case 0x26B1958CU /* ShiftLeft */: return RGFW_shiftL; /* 0x002A */ + case 0x36BF2438U /* Backslash */: return RGFW_backSlash; /* 0x002B */ + case 0x2C595B5AU /* KeyZ */: return RGFW_z; /* 0x002C */ + case 0x2C595B58U /* KeyX */: return RGFW_x; /* 0x002D */ + case 0x2C595B43U /* KeyC */: return RGFW_c; /* 0x002E */ + case 0x2C595B56U /* KeyV */: return RGFW_v; /* 0x002F */ + case 0x2C595B42U /* KeyB */: return RGFW_b; /* 0x0030 */ + case 0x2C595B4EU /* KeyN */: return RGFW_n; /* 0x0031 */ + case 0x2C595B4DU /* KeyM */: return RGFW_m; /* 0x0032 */ + case 0x92E1A1C1U /* Comma */: return RGFW_comma; /* 0x0033 */ + case 0x672FFAD4U /* Period */: return RGFW_period; /* 0x0034 */ + case 0x92E0A438U /* Slash */: return RGFW_slash; /* 0x0035 */ + case 0xC5A6BF7CU /* ShiftRight */: return RGFW_shiftR; + case 0x5D64DA91U /* NumpadMultiply */: return RGFW_kpMultiply; + case 0xC914958CU /* AltLeft */: return RGFW_altL; /* 0x0038 */ + case 0x92E09CB5U /* Space */: return RGFW_space; /* 0x0039 */ + case 0xB8FAE73BU /* CapsLock */: return RGFW_capsLock; /* 0x003A */ + case 0x7174B789U /* F1 */: return RGFW_F1; /* 0x003B */ + case 0x7174B78AU /* F2 */: return RGFW_F2; /* 0x003C */ + case 0x7174B78BU /* F3 */: return RGFW_F3; /* 0x003D */ + case 0x7174B78CU /* F4 */: return RGFW_F4; /* 0x003E */ + case 0x7174B78DU /* F5 */: return RGFW_F5; /* 0x003F */ + case 0x7174B78EU /* F6 */: return RGFW_F6; /* 0x0040 */ + case 0x7174B78FU /* F7 */: return RGFW_F7; /* 0x0041 */ + case 0x7174B780U /* F8 */: return RGFW_F8; /* 0x0042 */ + case 0x7174B781U /* F9 */: return RGFW_F9; /* 0x0043 */ + case 0x7B8E57B0U /* F10 */: return RGFW_F10; /* 0x0044 */ + case 0xC925FCDFU /* Numpad7 */: return RGFW_kpMultiply; /* 0x0047 */ + case 0xC925FCD0U /* Numpad8 */: return RGFW_kp8; /* 0x0048 */ + case 0xC925FCD1U /* Numpad9 */: return RGFW_kp9; /* 0x0049 */ + case 0x5EA3E8A4U /* NumpadSubtract */: return RGFW_minus; /* 0x004A */ + case 0xC925FCDCU /* Numpad4 */: return RGFW_kp4; /* 0x004B */ + case 0xC925FCDDU /* Numpad5 */: return RGFW_kp5; /* 0x004C */ + case 0xC925FCDEU /* Numpad6 */: return RGFW_kp6; /* 0x004D */ + case 0xC925FCD9U /* Numpad1 */: return RGFW_kp1; /* 0x004F */ + case 0xC925FCDAU /* Numpad2 */: return RGFW_kp2; /* 0x0050 */ + case 0xC925FCDBU /* Numpad3 */: return RGFW_kp3; /* 0x0051 */ + case 0xC925FCD8U /* Numpad0 */: return RGFW_kp0; /* 0x0052 */ + case 0x95852DACU /* NumpadDecimal */: return RGFW_period; /* 0x0053 */ + case 0x7B8E57B1U /* F11 */: return RGFW_F11; /* 0x0057 */ + case 0x7B8E57B2U /* F12 */: return RGFW_F12; /* 0x0058 */ + case 0x7B8E57B3U /* F13 */: return DOM_PK_F13; /* 0x0064 */ + case 0x7B8E57B4U /* F14 */: return DOM_PK_F14; /* 0x0065 */ + case 0x7B8E57B5U /* F15 */: return DOM_PK_F15; /* 0x0066 */ + case 0x7B8E57B6U /* F16 */: return DOM_PK_F16; /* 0x0067 */ + case 0x7B8E57B7U /* F17 */: return DOM_PK_F17; /* 0x0068 */ + case 0x7B8E57B8U /* F18 */: return DOM_PK_F18; /* 0x0069 */ + case 0x7B8E57B9U /* F19 */: return DOM_PK_F19; /* 0x006A */ + case 0x7B8E57A8U /* F20 */: return DOM_PK_F20; /* 0x006B */ + case 0x7B8E57A9U /* F21 */: return DOM_PK_F21; /* 0x006C */ + case 0x7B8E57AAU /* F22 */: return DOM_PK_F22; /* 0x006D */ + case 0x7B8E57ABU /* F23 */: return DOM_PK_F23; /* 0x006E */ + case 0x7393FBACU /* NumpadEqual */: return RGFW_kpReturn; + case 0xB88EBF7CU /* AltRight */: return RGFW_altR; /* 0xE038 */ + case 0xC925873BU /* NumLock */: return RGFW_numLock; /* 0xE045 */ + case 0x2C595F45U /* Home */: return RGFW_home; /* 0xE047 */ + case 0xC91BB690U /* ArrowUp */: return RGFW_up; /* 0xE048 */ + case 0x672F9210U /* PageUp */: return RGFW_pageUp; /* 0xE049 */ + case 0x3799258CU /* ArrowLeft */: return RGFW_left; /* 0xE04B */ + case 0x4CE33F7CU /* ArrowRight */: return RGFW_right; /* 0xE04D */ + case 0x7B8E55DCU /* End */: return RGFW_end; /* 0xE04F */ + case 0x3799379EU /* ArrowDown */: return RGFW_down; /* 0xE050 */ + case 0xBA90179EU /* PageDown */: return RGFW_pageDown; /* 0xE051 */ + case 0x6723CB2CU /* Insert */: return RGFW_insert; /* 0xE052 */ + case 0x6725C50DU /* Delete */: return RGFW_delete; /* 0xE053 */ + case 0x6723658CU /* OSLeft */: return RGFW_superL; /* 0xE05B */ + case 0x39643F7CU /* MetaRight */: return RGFW_superR; /* 0xE05C */ + case 0x380B9C8CU /* NumpadAdd */: return DOM_PK_NUMPAD_ADD; /* 0x004E */ + default: return DOM_PK_UNKNOWN; + } + + return 0; +} + +/* unsupported functions */ +void RGFW_window_focus(RGFW_window* win) { RGFW_UNUSED(win); } +void RGFW_window_raise(RGFW_window* win) { RGFW_UNUSED(win); } +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); return RGFW_FALSE; } +RGFW_monitor* RGFW_getMonitors(size_t* len) { RGFW_UNUSED(len); return NULL; } +RGFW_monitor RGFW_getPrimaryMonitor(void) { return (RGFW_monitor){}; } +void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { RGFW_UNUSED(win); RGFW_UNUSED(x); RGFW_UNUSED(y); } +void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); } +void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); } +void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { RGFW_UNUSED(win); RGFW_UNUSED(w); RGFW_UNUSED(h); } +void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win); } +void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win); } +void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_UNUSED(win); RGFW_UNUSED(floating); } +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_UNUSED(win); RGFW_UNUSED(border); } +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, RGFW_icon type) { RGFW_UNUSED(win); RGFW_UNUSED(data); RGFW_UNUSED(w); RGFW_UNUSED(h); RGFW_UNUSED(format); RGFW_UNUSED(type); return RGFW_FALSE; } +void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win); } +void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win); } +void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { RGFW_UNUSED(win); RGFW_UNUSED(request); } +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } +RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } +RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } +RGFW_bool RGFW_window_isFloating(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; } +RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win); return (RGFW_monitor){}; } +void RGFW_waitForEvent(i32 waitMS) { RGFW_UNUSED(waitMS); } +#endif + +/* end of web asm defines */ + +/* + * RGFW function pointer backend, made to allow you to compile for Wayland but fallback to X11 +*/ +#ifdef RGFW_DYNAMIC +typedef RGFW_window* (*RGFW_createWindowPlatform_ptr)(const char* name, RGFW_windowFlags flags, RGFW_window* win); +typedef RGFW_bool (*RGFW_getMouse_ptr)(i32* x, i32* y); +typedef u8 (*RGFW_rgfwToKeyChar_ptr)(u32 key); +typedef void (*RGFW_pollEvents_ptr)(void); +typedef void (*RGFW_window_move_ptr)(RGFW_window* win, i32 x, i32 y); +typedef void (*RGFW_window_resize_ptr)(RGFW_window* win, i32 w, i32 h); +typedef void (*RGFW_window_setAspectRatio_ptr)(RGFW_window* win, i32 w, i32 h); +typedef void (*RGFW_window_setMinSize_ptr)(RGFW_window* win, i32 w, i32 h); +typedef void (*RGFW_window_setMaxSize_ptr)(RGFW_window* win, i32 w, i32 h); +typedef void (*RGFW_window_maximize_ptr)(RGFW_window* win); +typedef void (*RGFW_window_focus_ptr)(RGFW_window* win); +typedef void (*RGFW_window_raise_ptr)(RGFW_window* win); +typedef void (*RGFW_window_setFullscreen_ptr)(RGFW_window* win, RGFW_bool fullscreen); +typedef void (*RGFW_window_setFloating_ptr)(RGFW_window* win, RGFW_bool floating); +typedef void (*RGFW_window_setOpacity_ptr)(RGFW_window* win, u8 opacity); +typedef void (*RGFW_window_minimize_ptr)(RGFW_window* win); +typedef void (*RGFW_window_restore_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_window_isFloating_ptr)(RGFW_window* win); +typedef void (*RGFW_window_setName_ptr)(RGFW_window* win, const char* name); +typedef void (*RGFW_window_setMousePassthrough_ptr)(RGFW_window* win, RGFW_bool passthrough); +typedef RGFW_bool (*RGFW_window_setIconEx_ptr)(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, u8 type); +typedef RGFW_mouse* (*RGFW_loadMouse_ptr)(u8* data, i32 w, i32 h, RGFW_format format); +typedef void (*RGFW_window_setMouse_ptr)(RGFW_window* win, RGFW_mouse* mouse); +typedef void (*RGFW_window_moveMouse_ptr)(RGFW_window* win, i32 x, i32 y); +typedef RGFW_bool (*RGFW_window_setMouseDefault_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_window_setMouseStandard_ptr)(RGFW_window* win, u8 mouse); +typedef void (*RGFW_window_hide_ptr)(RGFW_window* win); +typedef void (*RGFW_window_show_ptr)(RGFW_window* win); +typedef void (*RGFW_window_flash_ptr)(RGFW_window* win, RGFW_flashRequest request); +typedef RGFW_ssize_t (*RGFW_readClipboardPtr_ptr)(char* str, size_t strCapacity); +typedef void (*RGFW_writeClipboard_ptr)(const char* text, u32 textLen); +typedef RGFW_bool (*RGFW_window_isHidden_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_window_isMinimized_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_window_isMaximized_ptr)(RGFW_window* win); +typedef RGFW_monitor* (*RGFW_getMonitors_ptr)(size_t* len); +typedef RGFW_monitor (*RGFW_getPrimaryMonitor_ptr)(void); +typedef RGFW_bool (*RGFW_monitor_requestMode_ptr)(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request); +typedef RGFW_monitor (*RGFW_window_getMonitor_ptr)(RGFW_window* win); +typedef void (*RGFW_window_closePlatform_ptr)(RGFW_window* win); +typedef RGFW_bool (*RGFW_createSurfacePtr_ptr)(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface); +typedef void (*RGFW_window_blitSurface_ptr)(RGFW_window* win, RGFW_surface* surface); +typedef void (*RGFW_surface_freePtr_ptr)(RGFW_surface* surface); +typedef void (*RGFW_freeMouse_ptr)(RGFW_mouse* mouse); +typedef void (*RGFW_window_setBorder_ptr)(RGFW_window* win, RGFW_bool border); +typedef void (*RGFW_window_captureMousePlatform_ptr)(RGFW_window* win, RGFW_bool state); +typedef void (*RGFW_window_setRawMouseModePlatform_ptr)(RGFW_window* win, RGFW_bool state); +#ifdef RGFW_OPENGL +typedef void (*RGFW_window_makeCurrentContext_OpenGL_ptr)(RGFW_window* win); +typedef void* (*RGFW_getCurrentContext_OpenGL_ptr)(void); +typedef void (*RGFW_window_swapBuffers_OpenGL_ptr)(RGFW_window* win); +typedef void (*RGFW_window_swapInterval_OpenGL_ptr)(RGFW_window* win, i32 swapInterval); +typedef RGFW_bool (*RGFW_extensionSupportedPlatform_OpenGL_ptr)(const char* extension, size_t len); +typedef RGFW_proc (*RGFW_getProcAddress_OpenGL_ptr)(const char* procname); +typedef RGFW_bool (*RGFW_window_createContextPtr_OpenGL_ptr)(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints); +typedef void (*RGFW_window_deleteContextPtr_OpenGL_ptr)(RGFW_window* win, RGFW_glContext* ctx); +#endif +#ifdef RGFW_WEBGPU +typedef WGPUSurface (*RGFW_window_createSurface_WebGPU_ptr)(RGFW_window* window, WGPUInstance instance); +#endif + +/* Structure to hold all function pointers */ +typedef struct RGFW_FunctionPointers { + RGFW_createSurfacePtr_ptr createSurfacePtr; + RGFW_window_blitSurface_ptr window_blitSurface; + RGFW_surface_freePtr_ptr surface_freePtr; + RGFW_freeMouse_ptr freeMouse; + RGFW_window_setBorder_ptr window_setBorder; + RGFW_window_captureMousePlatform_ptr window_captureMousePlatform; + RGFW_window_setRawMouseModePlatform_ptr window_setRawMouseModePlatform; + RGFW_createWindowPlatform_ptr createWindowPlatform; + RGFW_getMouse_ptr getGlobalMouse; + RGFW_rgfwToKeyChar_ptr rgfwToKeyChar; + RGFW_pollEvents_ptr pollEvents; + RGFW_window_move_ptr window_move; + RGFW_window_resize_ptr window_resize; + RGFW_window_setAspectRatio_ptr window_setAspectRatio; + RGFW_window_setMinSize_ptr window_setMinSize; + RGFW_window_setMaxSize_ptr window_setMaxSize; + RGFW_window_maximize_ptr window_maximize; + RGFW_window_focus_ptr window_focus; + RGFW_window_raise_ptr window_raise; + RGFW_window_setFullscreen_ptr window_setFullscreen; + RGFW_window_setFloating_ptr window_setFloating; + RGFW_window_setOpacity_ptr window_setOpacity; + RGFW_window_minimize_ptr window_minimize; + RGFW_window_restore_ptr window_restore; + RGFW_window_isFloating_ptr window_isFloating; + RGFW_window_setName_ptr window_setName; + RGFW_window_setMousePassthrough_ptr window_setMousePassthrough; + RGFW_window_setIconEx_ptr window_setIconEx; + RGFW_loadMouse_ptr loadMouse; + RGFW_window_setMouse_ptr window_setMouse; + RGFW_window_moveMouse_ptr window_moveMouse; + RGFW_window_setMouseDefault_ptr window_setMouseDefault; + RGFW_window_setMouseStandard_ptr window_setMouseStandard; + RGFW_window_hide_ptr window_hide; + RGFW_window_show_ptr window_show; + RGFW_window_flash_ptr window_flash; + RGFW_readClipboardPtr_ptr readClipboardPtr; + RGFW_writeClipboard_ptr writeClipboard; + RGFW_window_isHidden_ptr window_isHidden; + RGFW_window_isMinimized_ptr window_isMinimized; + RGFW_window_isMaximized_ptr window_isMaximized; + RGFW_getMonitors_ptr getMonitors; + RGFW_getPrimaryMonitor_ptr getPrimaryMonitor; + RGFW_monitor_requestMode_ptr monitor_requestMode; + RGFW_window_getMonitor_ptr window_getMonitor; + RGFW_window_closePlatform_ptr window_closePlatform; +#ifdef RGFW_OPENGL + RGFW_extensionSupportedPlatform_OpenGL_ptr extensionSupportedPlatform_OpenGL; + RGFW_getProcAddress_OpenGL_ptr getProcAddress_OpenGL; + RGFW_window_createContextPtr_OpenGL_ptr window_createContextPtr_OpenGL; + RGFW_window_deleteContextPtr_OpenGL_ptr window_deleteContextPtr_OpenGL; + RGFW_window_makeCurrentContext_OpenGL_ptr window_makeCurrentContext_OpenGL; + RGFW_getCurrentContext_OpenGL_ptr getCurrentContext_OpenGL; + RGFW_window_swapBuffers_OpenGL_ptr window_swapBuffers_OpenGL; + RGFW_window_swapInterval_OpenGL_ptr window_swapInterval_OpenGL; +#endif +#ifdef RGFW_WEBGPU + RGFW_window_createSurface_WebGPU_ptr window_createSurface_WebGPU; +#endif +} RGFW_functionPointers; + +RGFW_functionPointers RGFW_api; + +RGFW_bool RGFW_createSurfacePtr(u8* data, i32 w, i32 h, RGFW_format format, RGFW_surface* surface) { return RGFW_api.createSurfacePtr(data, w, h, format, surface); } +void RGFW_surface_freePtr(RGFW_surface* surface) { RGFW_api.surface_freePtr(surface); } +void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_api.freeMouse(mouse); } +void RGFW_window_blitSurface(RGFW_window* win, RGFW_surface* surface) { RGFW_api.window_blitSurface(win, surface); } +void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_api.window_setBorder(win, border); } +void RGFW_window_captureMousePlatform(RGFW_window* win, RGFW_bool state) { RGFW_api.window_captureMousePlatform(win, state); } +void RGFW_window_setRawMouseModePlatform(RGFW_window* win, RGFW_bool state) { RGFW_api.window_setRawMouseModePlatform(win, state); } +RGFW_window* RGFW_createWindowPlatform(const char* name, RGFW_windowFlags flags, RGFW_window* win) { RGFW_init(); return RGFW_api.createWindowPlatform(name, flags, win); } +RGFW_bool RGFW_getGlobalMouse(i32* x, i32* y) { return RGFW_api.getGlobalMouse(x, y); } +u8 RGFW_rgfwToKeyChar(u32 key) { return RGFW_api.rgfwToKeyChar(key); } +void RGFW_pollEvents(void) { RGFW_api.pollEvents(); } +void RGFW_window_move(RGFW_window* win, i32 x, i32 y) { RGFW_api.window_move(win, x, y); } +void RGFW_window_resize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_resize(win, w, h); } +void RGFW_window_setAspectRatio(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setAspectRatio(win, w, h); } +void RGFW_window_setMinSize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setMinSize(win, w, h); } +void RGFW_window_setMaxSize(RGFW_window* win, i32 w, i32 h) { RGFW_api.window_setMaxSize(win, w, h); } +void RGFW_window_maximize(RGFW_window* win) { RGFW_api.window_maximize(win); } +void RGFW_window_focus(RGFW_window* win) { RGFW_api.window_focus(win); } +void RGFW_window_raise(RGFW_window* win) { RGFW_api.window_raise(win); } +void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) { RGFW_api.window_setFullscreen(win, fullscreen); } +void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_api.window_setFloating(win, floating); } +void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) { RGFW_api.window_setOpacity(win, opacity); } +void RGFW_window_minimize(RGFW_window* win) { RGFW_api.window_minimize(win); } +void RGFW_window_restore(RGFW_window* win) { RGFW_api.window_restore(win); } +RGFW_bool RGFW_window_isFloating(RGFW_window* win) { return RGFW_api.window_isFloating(win); } +void RGFW_window_setName(RGFW_window* win, const char* name) { RGFW_api.window_setName(win, name); } + +#ifndef RGFW_NO_PASSTHROUGH +void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) { RGFW_api.window_setMousePassthrough(win, passthrough); } +#endif + +RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, i32 w, i32 h, RGFW_format format, u8 type) { return RGFW_api.window_setIconEx(win, data, w, h, format, type); } +RGFW_mouse* RGFW_loadMouse(u8* data, i32 w, i32 h, RGFW_format format) { return RGFW_api.loadMouse(data, w, h, format); } +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { RGFW_api.window_setMouse(win, mouse); } +void RGFW_window_moveMouse(RGFW_window* win, i32 x, i32 y) { RGFW_api.window_moveMouse(win, x, y); } +RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) { return RGFW_api.window_setMouseDefault(win); } +RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { return RGFW_api.window_setMouseStandard(win, mouse); } +void RGFW_window_hide(RGFW_window* win) { RGFW_api.window_hide(win); } +void RGFW_window_show(RGFW_window* win) { RGFW_api.window_show(win); } +void RGFW_window_flash(RGFW_window* win, RGFW_flashRequest request) { RGFW_api.window_flash(win, request); } +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { return RGFW_api.readClipboardPtr(str, strCapacity); } +void RGFW_writeClipboard(const char* text, u32 textLen) { RGFW_api.writeClipboard(text, textLen); } +RGFW_bool RGFW_window_isHidden(RGFW_window* win) { return RGFW_api.window_isHidden(win); } +RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { return RGFW_api.window_isMinimized(win); } +RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { return RGFW_api.window_isMaximized(win); } +RGFW_monitor* RGFW_getMonitors(size_t* len) { return RGFW_api.getMonitors(len); } +RGFW_monitor RGFW_getPrimaryMonitor(void) { return RGFW_api.getPrimaryMonitor(); } +RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { return RGFW_api.monitor_requestMode(mon, mode, request); } +RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { return RGFW_api.window_getMonitor(win); } +void RGFW_window_closePlatform(RGFW_window* win) { RGFW_api.window_closePlatform(win); } + +#ifdef RGFW_OPENGL +RGFW_bool RGFW_extensionSupportedPlatform_OpenGL(const char* extension, size_t len) { return RGFW_api.extensionSupportedPlatform_OpenGL(extension, len); } +RGFW_proc RGFW_getProcAddress_OpenGL(const char* procname) { return RGFW_api.getProcAddress_OpenGL(procname); } +RGFW_bool RGFW_window_createContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx, RGFW_glHints* hints) { return RGFW_api.window_createContextPtr_OpenGL(win, ctx, hints); } +void RGFW_window_deleteContextPtr_OpenGL(RGFW_window* win, RGFW_glContext* ctx) { RGFW_api.window_deleteContextPtr_OpenGL(win, ctx); } +void RGFW_window_makeCurrentContext_OpenGL(RGFW_window* win) { RGFW_api.window_makeCurrentContext_OpenGL(win); } +void* RGFW_getCurrentContext_OpenGL(void) { return RGFW_api.getCurrentContext_OpenGL(); } +void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { RGFW_api.window_swapBuffers_OpenGL(win); } +void RGFW_window_swapInterval_OpenGL(RGFW_window* win, i32 swapInterval) { RGFW_api.window_swapInterval_OpenGL(win, swapInterval); } +#endif + +#ifdef RGFW_WEBGPU +WGPUSurface RGFW_window_createSurface_WebGPU(RGFW_window* window, WGPUInstance instance) { return RGFW_api.window_createSurface_WebGPU(window, instance); } +#endif +#endif /* RGFW_DYNAMIC */ + +/* + * start of X11 AND wayland defines + * this allows a single executable to support x11 AND wayland + * falling back to x11 if wayland fails to initalize +*/ +#if defined(RGFW_WAYLAND) && defined(RGFW_X11) +void RGFW_load_X11(void) { + RGFW_api.createSurfacePtr = RGFW_createSurfacePtr_X11; + RGFW_api.window_blitSurface = RGFW_window_blitSurface_X11; + RGFW_api.surface_freePtr = RGFW_surface_freePtr_X11; + RGFW_api.freeMouse = RGFW_freeMouse_X11; + RGFW_api.window_setBorder = RGFW_window_setBorder_X11; + RGFW_api.window_captureMousePlatform = RGFW_window_captureMousePlatform_X11; + RGFW_api.window_setRawMouseModePlatform = RGFW_window_setRawMouseModePlatform_X11; + RGFW_api.createWindowPlatform = RGFW_createWindowPlatform_X11; + RGFW_api.getGlobalMouse = RGFW_getGlobalMouse_X11; + RGFW_api.rgfwToKeyChar = RGFW_rgfwToKeyChar_X11; + RGFW_api.pollEvents = RGFW_pollEvents_X11; + RGFW_api.window_move = RGFW_window_move_X11; + RGFW_api.window_resize = RGFW_window_resize_X11; + RGFW_api.window_setAspectRatio = RGFW_window_setAspectRatio_X11; + RGFW_api.window_setMinSize = RGFW_window_setMinSize_X11; + RGFW_api.window_setMaxSize = RGFW_window_setMaxSize_X11; + RGFW_api.window_maximize = RGFW_window_maximize_X11; + RGFW_api.window_focus = RGFW_window_focus_X11; + RGFW_api.window_raise = RGFW_window_raise_X11; + RGFW_api.window_setFullscreen = RGFW_window_setFullscreen_X11; + RGFW_api.window_setFloating = RGFW_window_setFloating_X11; + RGFW_api.window_setOpacity = RGFW_window_setOpacity_X11; + RGFW_api.window_minimize = RGFW_window_minimize_X11; + RGFW_api.window_restore = RGFW_window_restore_X11; + RGFW_api.window_isFloating = RGFW_window_isFloating_X11; + RGFW_api.window_setName = RGFW_window_setName_X11; +#ifndef RGFW_NO_PASSTHROUGH + RGFW_api.window_setMousePassthrough = RGFW_window_setMousePassthrough_X11; +#endif + RGFW_api.window_setIconEx = RGFW_window_setIconEx_X11; + RGFW_api.loadMouse = RGFW_loadMouse_X11; + RGFW_api.window_setMouse = RGFW_window_setMouse_X11; + RGFW_api.window_moveMouse = RGFW_window_moveMouse_X11; + RGFW_api.window_setMouseDefault = RGFW_window_setMouseDefault_X11; + RGFW_api.window_setMouseStandard = RGFW_window_setMouseStandard_X11; + RGFW_api.window_hide = RGFW_window_hide_X11; + RGFW_api.window_show = RGFW_window_show_X11; + RGFW_api.window_flash = RGFW_window_flash_X11; + RGFW_api.readClipboardPtr = RGFW_readClipboardPtr_X11; + RGFW_api.writeClipboard = RGFW_writeClipboard_X11; + RGFW_api.window_isHidden = RGFW_window_isHidden_X11; + RGFW_api.window_isMinimized = RGFW_window_isMinimized_X11; + RGFW_api.window_isMaximized = RGFW_window_isMaximized_X11; + RGFW_api.getMonitors = RGFW_getMonitors_X11; + RGFW_api.getPrimaryMonitor = RGFW_getPrimaryMonitor_X11; + RGFW_api.monitor_requestMode = RGFW_monitor_requestMode_X11; + RGFW_api.window_getMonitor = RGFW_window_getMonitor_X11; + RGFW_api.window_closePlatform = RGFW_window_closePlatform_X11; +#ifdef RGFW_OPENGL + RGFW_api.extensionSupportedPlatform_OpenGL = RGFW_extensionSupportedPlatform_OpenGL_X11; + RGFW_api.getProcAddress_OpenGL = RGFW_getProcAddress_OpenGL_X11; + RGFW_api.window_createContextPtr_OpenGL = RGFW_window_createContextPtr_OpenGL_X11; + RGFW_api.window_deleteContextPtr_OpenGL = RGFW_window_deleteContextPtr_OpenGL_X11; + RGFW_api.window_makeCurrentContext_OpenGL = RGFW_window_makeCurrentContext_OpenGL_X11; + RGFW_api.getCurrentContext_OpenGL = RGFW_getCurrentContext_OpenGL_X11; + RGFW_api.window_swapBuffers_OpenGL = RGFW_window_swapBuffers_OpenGL_X11; + RGFW_api.window_swapInterval_OpenGL = RGFW_window_swapInterval_OpenGL_X11; +#endif +#ifdef RGFW_WEBGPU + RGFW_api.window_createSurface_WebGPU = RGFW_window_createSurface_WebGPU_X11; +#endif +} + +void RGFW_load_Wayland(void) { + RGFW_api.createSurfacePtr = RGFW_createSurfacePtr_Wayland; + RGFW_api.window_blitSurface = RGFW_window_blitSurface_Wayland; + RGFW_api.surface_freePtr = RGFW_surface_freePtr_Wayland; + RGFW_api.freeMouse = RGFW_freeMouse_Wayland; + RGFW_api.window_setBorder = RGFW_window_setBorder_Wayland; + RGFW_api.window_captureMousePlatform = RGFW_window_captureMousePlatform_Wayland; + RGFW_api.window_setRawMouseModePlatform = RGFW_window_setRawMouseModePlatform_Wayland; + RGFW_api.createWindowPlatform = RGFW_createWindowPlatform_Wayland; + RGFW_api.getGlobalMouse = RGFW_getGlobalMouse_Wayland; + RGFW_api.rgfwToKeyChar = RGFW_rgfwToKeyChar_Wayland; + RGFW_api.pollEvents = RGFW_pollEvents_Wayland; + RGFW_api.window_move = RGFW_window_move_Wayland; + RGFW_api.window_resize = RGFW_window_resize_Wayland; + RGFW_api.window_setAspectRatio = RGFW_window_setAspectRatio_Wayland; + RGFW_api.window_setMinSize = RGFW_window_setMinSize_Wayland; + RGFW_api.window_setMaxSize = RGFW_window_setMaxSize_Wayland; + RGFW_api.window_maximize = RGFW_window_maximize_Wayland; + RGFW_api.window_focus = RGFW_window_focus_Wayland; + RGFW_api.window_raise = RGFW_window_raise_Wayland; + RGFW_api.window_setFullscreen = RGFW_window_setFullscreen_Wayland; + RGFW_api.window_setFloating = RGFW_window_setFloating_Wayland; + RGFW_api.window_setOpacity = RGFW_window_setOpacity_Wayland; + RGFW_api.window_minimize = RGFW_window_minimize_Wayland; + RGFW_api.window_restore = RGFW_window_restore_Wayland; + RGFW_api.window_isFloating = RGFW_window_isFloating_Wayland; + RGFW_api.window_setName = RGFW_window_setName_Wayland; +#ifndef RGFW_NO_PASSTHROUGH + RGFW_api.window_setMousePassthrough = RGFW_window_setMousePassthrough_Wayland; +#endif + RGFW_api.window_setIconEx = RGFW_window_setIconEx_Wayland; + RGFW_api.loadMouse = RGFW_loadMouse_Wayland; + RGFW_api.window_setMouse = RGFW_window_setMouse_Wayland; + RGFW_api.window_moveMouse = RGFW_window_moveMouse_Wayland; + RGFW_api.window_setMouseDefault = RGFW_window_setMouseDefault_Wayland; + RGFW_api.window_setMouseStandard = RGFW_window_setMouseStandard_Wayland; + RGFW_api.window_hide = RGFW_window_hide_Wayland; + RGFW_api.window_show = RGFW_window_show_Wayland; + RGFW_api.window_flash = RGFW_window_flash_X11; + RGFW_api.readClipboardPtr = RGFW_readClipboardPtr_Wayland; + RGFW_api.writeClipboard = RGFW_writeClipboard_Wayland; + RGFW_api.window_isHidden = RGFW_window_isHidden_Wayland; + RGFW_api.window_isMinimized = RGFW_window_isMinimized_Wayland; + RGFW_api.window_isMaximized = RGFW_window_isMaximized_Wayland; + RGFW_api.getMonitors = RGFW_getMonitors_Wayland; + RGFW_api.getPrimaryMonitor = RGFW_getPrimaryMonitor_Wayland; + RGFW_api.monitor_requestMode = RGFW_monitor_requestMode_Wayland; + RGFW_api.window_getMonitor = RGFW_window_getMonitor_Wayland; + RGFW_api.window_closePlatform = RGFW_window_closePlatform_Wayland; +#ifdef RGFW_OPENGL + RGFW_api.extensionSupportedPlatform_OpenGL = RGFW_extensionSupportedPlatform_OpenGL_Wayland; + RGFW_api.getProcAddress_OpenGL = RGFW_getProcAddress_OpenGL_Wayland; + RGFW_api.window_createContextPtr_OpenGL = RGFW_window_createContextPtr_OpenGL_Wayland; + RGFW_api.window_deleteContextPtr_OpenGL = RGFW_window_deleteContextPtr_OpenGL_Wayland; + RGFW_api.window_makeCurrentContext_OpenGL = RGFW_window_makeCurrentContext_OpenGL_Wayland; + RGFW_api.getCurrentContext_OpenGL = RGFW_getCurrentContext_OpenGL_Wayland; + RGFW_api.window_swapBuffers_OpenGL = RGFW_window_swapBuffers_OpenGL_Wayland; + RGFW_api.window_swapInterval_OpenGL = RGFW_window_swapInterval_OpenGL_Wayland; +#endif +#ifdef RGFW_WEBGPU + RGFW_api.window_createSurface_WebGPU = RGFW_window_createSurface_WebGPU_Wayland; +#endif +} +#endif /* wayland AND x11 */ +/* end of X11 AND wayland defines */ + +#endif /* RGFW_IMPLEMENTATION */ + +#if defined(__cplusplus) && !defined(__EMSCRIPTEN__) +} +#endif + +#if _MSC_VER + #pragma warning( pop ) +#endif + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7d4ad99 --- /dev/null +++ b/src/main.c @@ -0,0 +1,35 @@ +#define RGFW_IMPLEMENTATION +#define RGFW_OPENGL +#include "RGFW.h" +#include + +void keyfunc (RGFW_window* win, RGFW_key key, u8 keychar, RGFW_keymod keymod, RGFW_bool repeat, RGFW_bool pressed){ + RGFW_UNUSED(repeat); + if (key == RGFW_escape && pressed){ + RGFW_window_setShouldClose(win, 1); + } +} + +int main (int argc, char **argv){ + RGFW_window* win = RGFW_createWindow("PACMAN CLONE", 0,0, 800, 600, RGFW_windowCenter | RGFW_windowNoResize | RGFW_windowOpenGL); + RGFW_setKeyCallback(keyfunc); + + while ( RGFW_window_shouldClose(win) == RGFW_FALSE){ + RGFW_event event; + RGFW_pollEvents(); + + + + + + + + } + + + + RGFW_window_close(win); + + return 0; +} +