Skip to content

Commit f948770

Browse files
nichcodenichcode
andauthored
Add X11 platform backend (#1)
* added linux core * add linux system * add linux thread * fixed CPU architecture * system cursors * x11 features * x11 monitor * linux monitor mode * simple x11 window * maximized window * window styles * resize and move event * window state event * stable window * window test X11 * icon window * update API * fix window state bug * resource * cursor window * mouse window * X11 video * add opengl system X11 * opengl window x11 * Linux backend cleanup * transparent icons and cursors * source format * tests format * updated readme * premake linux --------- Co-authored-by: nichcode <nicholasagbo04@gmail.com>
1 parent fdfd7ad commit f948770

37 files changed

+7610
-449
lines changed

CHANGELOG.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,35 @@
1414

1515
### Notes
1616
- No API or ABI changes
17-
- Safe upgrade from **v1.0** - just rebuild your project after updating.
17+
- Safe upgrade from **v1.0** - just rebuild your project after updating.
18+
19+
## [1.1.0] - 2025-10-17
20+
21+
### Features
22+
- **Build:** Added Linux platform support across all modules.
23+
- **Core:** Added Linux backend support.
24+
- **Video:** Added X11-based backend support.
25+
- **Thread:** Added Linux backend support.
26+
- **Opengl:** Added Linux backend support.
27+
- **System:** Added Linux backend support.
28+
- **Video:** Added **palCreateCursorFrom()** to create system cursors.
29+
- **Video:** Added **palSetFBConfig()** to select window FBConfig.
30+
- **Video:** Added **PAL_VIDEO_FEATURE_WINDOW_SET_ICON** to `PalVideoFeatures` enum.
31+
- **System:** Added **PAL_PLATFORM_API_COCOA** to `PalPlatformApiType` enum.
32+
- **System:** Added **PAL_PLATFORM_API_ANDRIOD** to `PalPlatformApiType` enum.
33+
- **System:** Added **PAL_PLATFORM_API_UIKIT** to `PalPlatformApiType` enum.
34+
- **System:** Added **PAL_PLATFORM_API_HEADLESS** to `PalPlatformApiType` enum.
35+
- **Core:** Added **PAL_RESULT_INVALID_FBCONFIG_BACKEND** to `PalResult` enum.
36+
37+
### Changed
38+
- **System:** `PalCPUInfo.architecture` is now determined at runtime instead of build time.
39+
- **Opengl:** **palEnumerateGLFBConfigs()** now does not use the `glWindow` paramter. Set to `nullptr`
40+
41+
### Fixed
42+
- Fixed a bug where **enter modal mode and exit modal mode** operations triggered only one event.
43+
- Fixed repeated window state event (**minimized**, **maximized**, **restore**).
44+
45+
### Notes
46+
- No API or ABI changes - existing Windows code remains compatible.
47+
- Linux video support currently targets **X11** only: **Wayland** is planned for future releases.
48+
- Safe upgrade from **v1.0.1** - just rebuild your project after updating.

README.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@
77

88
PAL is a lightweight, low-level, cross-platform abstraction layer in **C**, designed to be **explicit** and as close to the **OS** as possible — similar in philosophy to Vulkan. It gives you precise control without hidden behavior, making it ideal for developers who want performance and predictability.
99

10+
PAL is transparent. All queries — window size, position, monitor info, and more — reflect the current platform state. Using PAL is like working directly with the OS: it applies no hidden logic, makes no assumptions, and leaves behavior fully in your control.
11+
12+
This approach gives you total control: you handle events, manage resources, and cache state explicitly. PAL provides the building blocks; how you use them — whether for simple applications or advanced frameworks — is entirely up to you.
13+
14+
Example – Get Window Size
15+
```c
16+
// Direct query from the platform — not cached by PAL
17+
palGetWindowSize(window, &w, &h);
18+
```
19+
> Note: palGetWindowSize queries the OS directly. If your application needs continuous updates (e.g., window moves or resizes frequently), it is more efficient to listen to PAL events rather than repeatedly querying the OS. This ensures your app stays performant.
20+
1021
---
1122
1223
## Why PAL?
@@ -60,7 +71,11 @@ For more detailed examples, see the [tests folder](./tests) tests folder, which
6071
---
6172

6273
## Philosophy
63-
74+
- PAL is a thin layer over the OS, not a framework or library.
75+
- Queries return the current platform state, reflecting any changes made through direct OS calls.
76+
- Developers are responsible for state tracking, caching, and event handling.
77+
- PAL enables cross-platform consistency while preserving full OS behavior and control.
78+
- Advanced users can build libraries or frameworks on top of PAL.
6479
- Minimal overhead (close to raw OS calls)
6580
- Explicit API (no hidden behavior or defaults)
6681
- Event system supporting both polling and callbacks
@@ -73,9 +88,10 @@ For more detailed examples, see the [tests folder](./tests) tests folder, which
7388

7489
## Supported Platforms
7590
- Windows (Vista+)
91+
- Linux (X11)
7692

7793
## Planned Platforms
78-
- Linux (X11/Wayland)
94+
- Linux (Wayland)
7995
- macOS (Cocoa)
8096
- Android
8197
- iOS
@@ -84,6 +100,8 @@ For more detailed examples, see the [tests folder](./tests) tests folder, which
84100
- Standard C library
85101
- Platform SDKs (Win32, X11, Cocoa, etc.)
86102
- [Make for Windows](https://www.gnu.org/software/make/) (if not using Visual Studio)
103+
- XRandR (1.2+) for X11
104+
- libXcursor for X11
87105

88106
## Compilers
89107
- GCC
@@ -97,6 +115,7 @@ For more detailed examples, see the [tests folder](./tests) tests folder, which
97115
PAL is written in **C99** and uses **Premake** as its build system. Configure modules via [pal_config.lua](./pal_config.lua).
98116
See [pal_config.h](./include/pal/pal_config.h) to see the reflection of modules that will be built.
99117

118+
**Windows**
100119
```bash
101120
premake\premake5.exe gmake2 # generate Makefiles (default: GCC)
102121
premake\premake5.exe gmake2 --compiler=clang
@@ -105,6 +124,11 @@ premake\premake5.exe vs2022 # generate Visual Studio project (default: MS
105124
premake\premake5.exe vs2022 --compiler=clang
106125
```
107126

127+
**Linux**
128+
```bash
129+
./premake/premake5 gmake # generate Makefiles (default: GCC)
130+
```
131+
108132
Enable tests in `pal_config.lua` by setting `PAL_BUILD_TESTS = true`.
109133

110134
---
@@ -132,7 +156,7 @@ PAL uses [Doxygen](https://www.doxygen.nl/) for generating API documentation.
132156

133157
```bash
134158
cd docs
135-
make doxygen
159+
doxygen doxyfile
136160
```
137161

138162
The generated HTML docs will be available in `docs/html/`.

include/pal/pal_core.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ typedef enum {
236236
PAL_RESULT_INVALID_GL_FBCONFIG,
237237
PAL_RESULT_INVALID_GL_VERSION,
238238
PAL_RESULT_INVALID_GL_PROFILE,
239-
PAL_RESULT_INVALID_GL_CONTEXT
239+
PAL_RESULT_INVALID_GL_CONTEXT,
240+
PAL_RESULT_INVALID_FBCONFIG_BACKEND
240241
} PalResult;
241242

242243
/**

include/pal/pal_opengl.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ PAL_API const PalGLInfo* PAL_CALL palGetGLInfo();
261261
* If the count is 0 and the PalGLFBConfigs array is nullptr, the function fails
262262
* and returns `PAL_RESULT_INSUFFICIENT_BUFFER`.
263263
*
264-
* @param[in] glWindow Pointer to the opengl window.
264+
* @param[in] glWindow Set to nullptr.
265265
* @param[in] count Capacity of the PalGLFBConfig array.
266266
* @param[out] configs User allocated array of PalGLFBConfig.
267267
*
@@ -313,10 +313,9 @@ PAL_API const PalGLFBConfig* PAL_CALL palGetClosestGLFBConfig(
313313
* The opengl system must be initialized before this call. The created context
314314
* will not be made current.
315315
*
316-
* After this call, the provided PalGLFBConfig will be set to the window
317-
* permanently and cannot be changed. To change it, you must destroy the window
318-
* and recreate it. If the window already has a PalGLFBConfig, the opengl system
319-
* will use that and discard the provided one.
316+
* The provided PalGLFBConfig must be the same as the one used to create the
317+
* window. Once set, it cannot be changed. To change it, you must destroy the
318+
* window and recreate it.
320319
*
321320
* @param[in] info Pointer to a PalGLContextCreateInfo struct that specifies
322321
* paramters. Must not be nullptr.

include/pal/pal_system.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,11 @@ typedef enum {
119119
typedef enum {
120120
PAL_PLATFORM_API_WIN32,
121121
PAL_PLATFORM_API_WAYLAND,
122-
PAL_PLATFORM_API_X11
122+
PAL_PLATFORM_API_X11,
123+
PAL_PLATFORM_API_COCOA,
124+
PAL_PLATFORM_API_ANDRIOD,
125+
PAL_PLATFORM_API_UIKIT,
126+
PAL_PLATFORM_API_HEADLESS
123127
} PalPlatformApiType;
124128

125129
/**

include/pal/pal_video.h

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ typedef enum {
111111
PAL_VIDEO_FEATURE_WINDOW_GET_STYLE = PAL_BIT(28),
112112
PAL_VIDEO_FEATURE_CURSOR_SET_POS = PAL_BIT(29),
113113
PAL_VIDEO_FEATURE_CURSOR_GET_POS = PAL_BIT(30),
114+
PAL_VIDEO_FEATURE_WINDOW_SET_ICON = PAL_BIT(31),
114115
} PalVideoFeatures;
115116

116117
/**
@@ -186,6 +187,23 @@ typedef enum {
186187
PAL_FLASH_TRAY = PAL_BIT(1) /**< Flash the icon of the window.*/
187188
} PalFlashFlag;
188189

190+
/**
191+
* @enum PalFBConfigBackend
192+
* @brief Represents the backend of a FBConfig.
193+
*
194+
* All FBConfig backends follow the format `PAL_CONFIG_BACKEND**` for
195+
* consistency and API use.
196+
*
197+
* @since 1.1
198+
* @ingroup pal_video
199+
*/
200+
typedef enum {
201+
PAL_CONFIG_BACKEND_EGL,
202+
PAL_CONFIG_BACKEND_GLX,
203+
PAL_CONFIG_BACKEND_WGL,
204+
PAL_CONFIG_BACKEND_PAL_OPENGL /**< Use PAL opengl module backend.*/
205+
} PalFBConfigBackend;
206+
189207
/**
190208
* @enum PalScancode
191209
* @brief scancodes (layout independent keys) of a keyboard.
@@ -466,6 +484,26 @@ typedef enum {
466484
PAL_MOUSE_BUTTON_MAX
467485
} PalMouseButton;
468486

487+
/**
488+
* @enum PalCursorType
489+
* @brief System cursor types.
490+
*
491+
* All cursor types follow the format `PAL_CURSOR_**` for
492+
* consistency and API use.
493+
*
494+
* @since 1.1
495+
* @ingroup pal_video
496+
*/
497+
typedef enum {
498+
PAL_CURSOR_ARROW,
499+
PAL_CURSOR_HAND,
500+
PAL_CURSOR_CROSS,
501+
PAL_CURSOR_IBEAM,
502+
PAL_CURSOR_WAIT,
503+
504+
PAL_CURSOR_MAX
505+
} PalCursorType;
506+
469507
/**
470508
* @struct PalMonitorInfo
471509
* @brief Information about a monitor.
@@ -651,6 +689,41 @@ PAL_API void PAL_CALL palUpdateVideo();
651689
*/
652690
PAL_API PalVideoFeatures PAL_CALL palGetVideoFeatures();
653691

692+
/**
693+
* @brief Set the FBConfig for the video system.
694+
*
695+
* The video system must be initialized before this call.
696+
* The provided FBConfig will be used for all created windows after this call.
697+
* The `index` is the loop index from the drivers
698+
* supported FBConfigs.
699+
*
700+
* The `backend` is used to tell the video system, the source of the index.
701+
* Examples: PAL_CONFIG_BACKEND_EGL tells the video system, we got this loop
702+
* index from EGL. This will enable the video system to find your FBConfig.
703+
*
704+
* Example Flow:
705+
* Enumerate and select your FBConfig using any backend(EGL, GLX, WGL, etc)
706+
* and just let the video system know which one you used.
707+
*
708+
* If the backend passed is not the same as the one used,
709+
* the video system might still get a FBConfig but it will not be the
710+
* one requested.
711+
*
712+
* @param[in] index The FBConfig driver index.
713+
* @param[in] backend The FBConfig backend or source.
714+
*
715+
* @return `PAL_RESULT_SUCCESS` on success or a result code on
716+
* failure. Call palFormatResult() for more information.
717+
*
718+
* Thread safety: This function must be called from the main thread.
719+
*
720+
* @since 1.1
721+
* @ingroup pal_video
722+
*/
723+
PAL_API PalResult PAL_CALL palSetFBConfig(
724+
const int index,
725+
PalFBConfigBackend backend);
726+
654727
/**
655728
* @brief Return a list of all connected monitors.
656729
*
@@ -1451,6 +1524,7 @@ PAL_API PalResult PAL_CALL palSetFocusWindow(PalWindow* window);
14511524
* @brief Create an icon.
14521525
*
14531526
* The video system must be initialized before this call.
1527+
* `PAL_VIDEO_FEATURE_WINDOW_SET_ICON` must be supported.
14541528
*
14551529
* @param[in] info Pointer to a PalIconCreateInfo struct that specifies
14561530
* paramters. Must not be nullptr.
@@ -1492,9 +1566,10 @@ PAL_API void PAL_CALL palDestroyIcon(PalIcon* icon);
14921566
* @brief Set the icon for the provided window.
14931567
*
14941568
* The video system must be initialized before this call.
1569+
* `PAL_VIDEO_FEATURE_WINDOW_SET_ICON` must be supported.
14951570
*
14961571
* @param[in] window Pointer to the window.
1497-
* @param[in] icon Pointer to the icon.
1572+
* @param[in] icon Pointer to the icon. Set to nullptr to revert.
14981573
*
14991574
* @return `PAL_RESULT_SUCCESS` on success or a result code on
15001575
* failure. Call palFormatResult() for more information.
@@ -1531,6 +1606,28 @@ PAL_API PalResult PAL_CALL palCreateCursor(
15311606
const PalCursorCreateInfo* info,
15321607
PalCursor** outCursor);
15331608

1609+
/**
1610+
* @brief Create a system cursor.
1611+
*
1612+
* The video system must be initialized before this call.
1613+
*
1614+
* @param[in] type The system cursor type to create. Must not be nullptr.
1615+
* @param[out] outCursor Pointer to a PalCursor to recieve the created
1616+
* cursor. Must not be nullptr.
1617+
*
1618+
* @return `PAL_RESULT_SUCCESS` on success or a result code on
1619+
* failure. Call palFormatResult() for more information.
1620+
*
1621+
* Thread safety: This function must only be called from the main thread.
1622+
*
1623+
* @since 1.1
1624+
* @ingroup pal_video
1625+
* @sa palDestroyCursor
1626+
*/
1627+
PAL_API PalResult PAL_CALL palCreateCursorFrom(
1628+
PalCursorType type,
1629+
PalCursor** outCursor);
1630+
15341631
/**
15351632
* @brief Destroy the provided cursor.
15361633
*
@@ -1550,7 +1647,7 @@ PAL_API PalResult PAL_CALL palCreateCursor(
15501647
PAL_API void PAL_CALL palDestroyCursor(PalCursor* cursor);
15511648

15521649
/**
1553-
* @brief Show or hide the provided cursor.
1650+
* @brief Show or hide the cursor.
15541651
*
15551652
* The video system must be initialized before this call.
15561653
* This affects all created cursors since the platform (OS) merges all cursors
@@ -1646,7 +1743,7 @@ PAL_API PalResult PAL_CALL palSetCursorPos(
16461743
* The video system must be initialized before this call.
16471744
*
16481745
* @param[in] window Pointer to the window.
1649-
* @param[in] cursor Pointer to the cursor.
1746+
* @param[in] cursor Pointer to the cursor. Set to nullptr to revert.
16501747
*
16511748
* @return `PAL_RESULT_SUCCESS` on success or a result code on
16521749
* failure. Call palFormatResult() for more information.

pal.lua

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,61 @@ project "PAL"
6161

6262
if (PAL_BUILD_SYSTEM) then
6363
filter {"system:windows", "configurations:*"}
64-
files { "src/system/pal_system_win32.c" }
64+
files { "src/system/pal_system_win32.c" }
65+
66+
filter {"system:linux", "configurations:*"}
67+
files { "src/system/pal_system_linux.c" }
6568
filter {}
6669
end
6770

6871
if (PAL_BUILD_THREAD) then
6972
filter {"system:windows", "configurations:*"}
70-
files { "src/thread/pal_thread_win32.c" }
73+
files { "src/thread/pal_thread_win32.c" }
74+
75+
filter {"system:linux", "configurations:*"}
76+
files { "src/thread/pal_thread_linux.c" }
77+
7178
filter {}
7279
end
7380

7481
if (PAL_BUILD_VIDEO) then
7582
filter {"system:windows", "configurations:*"}
76-
files { "src/video/pal_video_win32.c" }
83+
files { "src/video/pal_video_win32.c" }
84+
85+
filter {"system:linux", "configurations:*"}
86+
files { "src/video/pal_video_linux.c" }
87+
88+
-- check for wayland support. This is cross compiler
89+
local paths = {
90+
"/usr/include/wayland-client.h",
91+
"/usr/include/x86_64-linux-gnu/wayland-client.h"
92+
}
93+
94+
local found = false
95+
for _, path in ipairs(paths) do
96+
local file = io.open(path, "r")
97+
if file then
98+
file:close()
99+
found = true
100+
break
101+
end
102+
end
103+
104+
if found then
105+
defines { "PAL_HAS_WAYLAND=1" }
106+
else
107+
defines { "PAL_HAS_WAYLAND=0" }
108+
end
109+
77110
filter {}
78111
end
79112

80113
if (PAL_BUILD_OPENGL) then
81114
filter {"system:windows", "configurations:*"}
82-
files { "src/opengl/pal_opengl_win32.c" }
115+
files { "src/opengl/pal_opengl_win32.c" }
116+
117+
filter {"system:linux", "configurations:*"}
118+
files { "src/opengl/pal_opengl_linux.c" }
83119
filter {}
84120
end
85121

premake/premake5

2.54 MB
Binary file not shown.

0 commit comments

Comments
 (0)