$ -weight: 600;">sudo -weight: 500;">apt -weight: 500;">install libx11-dev libx11-doc
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install libx11-dev libx11-doc
-weight: 600;">sudo -weight: 500;">apt -weight: 500;">install libx11-dev libx11-doc
man XOpenDisplay
man XOpenDisplay
man XOpenDisplay
man 3 XOpenDisplay
man 3 XOpenDisplay
man 3 XOpenDisplay
#include <stdio.h>
#define XLIB_ILLEGAL_ACCESS 1
#include <X11/Xlib.h>
#include <stdio.h>
#define XLIB_ILLEGAL_ACCESS 1
#include <X11/Xlib.h>
#include <stdio.h>
#define XLIB_ILLEGAL_ACCESS 1
#include <X11/Xlib.h>
Display *display = XOpenDisplay(NULL);
if (!display) { fprintf(stderr, "%s", "failed to connect to the X Window Server\n"); return 1;
}
Display *display = XOpenDisplay(NULL);
if (!display) { fprintf(stderr, "%s", "failed to connect to the X Window Server\n"); return 1;
}
Display *display = XOpenDisplay(NULL);
if (!display) { fprintf(stderr, "%s", "failed to connect to the X Window Server\n"); return 1;
}
Window XCreateSimpleWindow( Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, unsigned long border, unsigned long background);
Window XCreateSimpleWindow( Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, unsigned long border, unsigned long background);
Window XCreateSimpleWindow( Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, unsigned long border, unsigned long background);
Screen *screen = DefaultScreenOfDisplay(display);
int width = WidthOfScreen(screen);
int height = HeightOfScreen(screen);
Screen *screen = DefaultScreenOfDisplay(display);
int width = WidthOfScreen(screen);
int height = HeightOfScreen(screen);
Screen *screen = DefaultScreenOfDisplay(display);
int width = WidthOfScreen(screen);
int height = HeightOfScreen(screen);
unsigned long BlackPixelValue = BlackPixelOfScreen(screen);
unsigned long BlackPixelValue = BlackPixelOfScreen(screen);
unsigned long BlackPixelValue = BlackPixelOfScreen(screen);
Screen *screen = DefaultScreenOfDisplay(display);
Window window = XCreateSimpleWindow( display, DefaultRootWindow(display), 0, 0, WidthOfScreen(screen), HeightOfScreen(screen), 0, BlackPixelOfScreen(screen), BlackPixelOfScreen(screen)
);
Screen *screen = DefaultScreenOfDisplay(display);
Window window = XCreateSimpleWindow( display, DefaultRootWindow(display), 0, 0, WidthOfScreen(screen), HeightOfScreen(screen), 0, BlackPixelOfScreen(screen), BlackPixelOfScreen(screen)
);
Screen *screen = DefaultScreenOfDisplay(display);
Window window = XCreateSimpleWindow( display, DefaultRootWindow(display), 0, 0, WidthOfScreen(screen), HeightOfScreen(screen), 0, BlackPixelOfScreen(screen), BlackPixelOfScreen(screen)
);
(gdb) p *display
$1 = { fd = 3, proto_major_version = 11, proto_minor_version = 0, vendor = 0x406590 "The X.Org Foundation", last_request_read = 6, request = 7, display_name = 0x406500 ":0", default_screen = 0, nscreens = 1, screens = 0x406a10, min_keycode = 8, max_keycode = 255,
}
(gdb) p *display
$1 = { fd = 3, proto_major_version = 11, proto_minor_version = 0, vendor = 0x406590 "The X.Org Foundation", last_request_read = 6, request = 7, display_name = 0x406500 ":0", default_screen = 0, nscreens = 1, screens = 0x406a10, min_keycode = 8, max_keycode = 255,
}
(gdb) p *display
$1 = { fd = 3, proto_major_version = 11, proto_minor_version = 0, vendor = 0x406590 "The X.Org Foundation", last_request_read = 6, request = 7, display_name = 0x406500 ":0", default_screen = 0, nscreens = 1, screens = 0x406a10, min_keycode = 8, max_keycode = 255,
}
XStoreName(display, window, "Handmade Hero");
XStoreName(display, window, "Handmade Hero");
XStoreName(display, window, "Handmade Hero");
int
XStoreName ( register Display *dpy, Window w, _Xconst char *name)
{ if (name != NULL && strlen(name) >= USHRT_MAX) return 0; return XChangeProperty(dpy, w, XA_WM_NAME, XA_STRING, /* */ 8, PropModeReplace, (_Xconst unsigned char *)name, name ? (int) strlen(name) : 0);
}
int
XStoreName ( register Display *dpy, Window w, _Xconst char *name)
{ if (name != NULL && strlen(name) >= USHRT_MAX) return 0; return XChangeProperty(dpy, w, XA_WM_NAME, XA_STRING, /* */ 8, PropModeReplace, (_Xconst unsigned char *)name, name ? (int) strlen(name) : 0);
}
int
XStoreName ( register Display *dpy, Window w, _Xconst char *name)
{ if (name != NULL && strlen(name) >= USHRT_MAX) return 0; return XChangeProperty(dpy, w, XA_WM_NAME, XA_STRING, /* */ 8, PropModeReplace, (_Xconst unsigned char *)name, name ? (int) strlen(name) : 0);
}
XSetWindowAttributes template = {};
template.event_mask = ExposureMask;
XSetWindowAttributes template = {};
template.event_mask = ExposureMask;
XSetWindowAttributes template = {};
template.event_mask = ExposureMask;
template.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask;
template.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask;
template.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask;
#define NoEventMask 0L
#define KeyPressMask (1L<<0)
#define KeyReleaseMask (1L<<1)
#define ExposureMask (1L<<15)
#define NoEventMask 0L
#define KeyPressMask (1L<<0)
#define KeyReleaseMask (1L<<1)
#define ExposureMask (1L<<15)
#define NoEventMask 0L
#define KeyPressMask (1L<<0)
#define KeyReleaseMask (1L<<1)
#define ExposureMask (1L<<15)
int XChangeWindowAttributes( Display *display, Window w, unsigned long valuemask, XSetWindowAttributes *attributes
);
int XChangeWindowAttributes( Display *display, Window w, unsigned long valuemask, XSetWindowAttributes *attributes
);
int XChangeWindowAttributes( Display *display, Window w, unsigned long valuemask, XSetWindowAttributes *attributes
);
#define CWEventMask (1L<<11)
#define CWEventMask (1L<<11)
#define CWEventMask (1L<<11)
XChangeWindowAttributes(display, window, CWEventMask, &template);
XChangeWindowAttributes(display, window, CWEventMask, &template);
XChangeWindowAttributes(display, window, CWEventMask, &template);
XMapWindow(display, window);
XMapWindow(display, window);
XMapWindow(display, window);
(gdb) p *display
$2 = { fd = 3, proto_major_version = 11, proto_minor_version = 0, vendor = 0x406590 "The X.Org Foundation", last_request_read = 6, request = 10, display_name = 0x406500 ":0", default_screen = 0, nscreens = 1, screens = 0x406a10, min_keycode = 8, max_keycode = 255,
}
(gdb) p *display
$2 = { fd = 3, proto_major_version = 11, proto_minor_version = 0, vendor = 0x406590 "The X.Org Foundation", last_request_read = 6, request = 10, display_name = 0x406500 ":0", default_screen = 0, nscreens = 1, screens = 0x406a10, min_keycode = 8, max_keycode = 255,
}
(gdb) p *display
$2 = { fd = 3, proto_major_version = 11, proto_minor_version = 0, vendor = 0x406590 "The X.Org Foundation", last_request_read = 6, request = 10, display_name = 0x406500 ":0", default_screen = 0, nscreens = 1, screens = 0x406a10, min_keycode = 8, max_keycode = 255,
}
int XWindowEvent(Display *display, Window w, long event_mask, XEvent *event_return);
int XWindowEvent(Display *display, Window w, long event_mask, XEvent *event_return);
int XWindowEvent(Display *display, Window w, long event_mask, XEvent *event_return);
XEvent ev = {};
XWindowEvent(display, window, ExposureMask, &ev);
XEvent ev = {};
XWindowEvent(display, window, ExposureMask, &ev);
XEvent ev = {};
XWindowEvent(display, window, ExposureMask, &ev);
(gdb) p *display
$3 = { fd = 3, proto_major_version = 11, proto_minor_version = 0, vendor = 0x406590 "The X.Org Foundation", last_request_read = 10, request = 10, display_name = 0x406500 ":0", default_screen = 0, nscreens = 1, screens = 0x406a10, min_keycode = 8, max_keycode = 255,
}
(gdb) p *display
$3 = { fd = 3, proto_major_version = 11, proto_minor_version = 0, vendor = 0x406590 "The X.Org Foundation", last_request_read = 10, request = 10, display_name = 0x406500 ":0", default_screen = 0, nscreens = 1, screens = 0x406a10, min_keycode = 8, max_keycode = 255,
}
(gdb) p *display
$3 = { fd = 3, proto_major_version = 11, proto_minor_version = 0, vendor = 0x406590 "The X.Org Foundation", last_request_read = 10, request = 10, display_name = 0x406500 ":0", default_screen = 0, nscreens = 1, screens = 0x406a10, min_keycode = 8, max_keycode = 255,
}
while (1) { XEvent ev = {}; if (XCheckWindowEvent(display, window, ExposureMask, &ev)) { break; }
}
while (1) { XEvent ev = {}; if (XCheckWindowEvent(display, window, ExposureMask, &ev)) { break; }
}
while (1) { XEvent ev = {}; if (XCheckWindowEvent(display, window, ExposureMask, &ev)) { break; }
}
char c = 0;
fprintf(stdout, "%s", "game paused, press enter to continue\n");
fread(&c, sizeof(c), 1, stdin);
char c = 0;
fprintf(stdout, "%s", "game paused, press enter to continue\n");
fread(&c, sizeof(c), 1, stdin);
char c = 0;
fprintf(stdout, "%s", "game paused, press enter to continue\n");
fread(&c, sizeof(c), 1, stdin);
XDestroyWindow(display, window);
XDestroyWindow(display, window);
XDestroyWindow(display, window);
XCloseDisplay(display);
XCloseDisplay(display);
XCloseDisplay(display);
/* Copyright (c) 2026 Misael Díaz-Maldonado Distributed under the MIT License. See the MIT LICENSE URL https://opensource.org/license/mit for details.
*/
#include <stdio.h>
#include <X11/Xlib.h> int main() {
/* Think of opening the display as connecting to the default session of the X11 server. When you pass zero or NULL to this function it tells the client to lookup the default from your system configuration; more specifically the shell environment variable DISPLAY. This is more portable and less error-prone than typing it yourself. If there is an error the display will be NULL and so the application informs the user about the problem and returns the general purpose error code.
*/ Display *display = XOpenDisplay(NULL); if (!display) { fprintf(stderr, "%s", "failed to open display\n"); return 1; }
/* It is important to keep in mind that in X11 all windows must have a parent window except the root window. So the easiest way to create a window is by using the macro that returns the parent window. You can see that other macros were used to determine dimensions of the window and the color for your screen. This is the most portable way of creating a window with Xlib. As you gain more experience you can use the more advanced version XCreateWindow.
*/ Screen *screen = DefaultScreenOfDisplay(display); Window window = XCreateSimpleWindow( display, DefaultRootWindow(display), 0, 0, WidthOfScreen(screen), HeightOfScreen(screen), 0, BlackPixelOfScreen(screen), BlackPixelOfScreen(screen) ); /* Setting Window Properties and Attributes. Naming the window feels good; knowing that the name is stored on the server-side along with other window properties makes you think of X11 window application development as modifying the state of a state machine. This is good to have in mind because not all Xlib calls involve visible changes on your screen. We need to tell the server that we care about Expose events to be able to know how much we need to wait on the client side for the event to happen, this reveals the asynchronous nature of the X11 windowing system.
*/ XStoreName(display, window, "Handmade Hero"); XSetWindowAttributes template = {}; template.event_mask = ExposureMask; XChangeWindowAttributes(display, window, CWEventMask, &template); /* Here we tell the server to map our game window into our screen but that alone won't make it visible for it is a request that gets buffered locally for performance (client-server roundtrips are expensive).
*/ XMapWindow(display, window); /* Use polling to handle async XWayland communications with the client while retaining backwards compatibility with X11-based Linux desktops. The client waits for the Expose event to happen so that the game window becomes visible. Note that without blocking the client by reading from standard input the game window may not have time to show up before we close the connection to the server. It is recommended to throttle the CPU by sleeping for a suitable time interval at the end of each while-loop cycle; otherwise, many CPU cycles will be wasted on checking window events too frequently.
*/ while (1) { XEvent ev = {}; if (XCheckWindowEvent(display, window, ExposureMask, &ev)) { break; } } char c = 0; fprintf(stdout, "%s", "game paused, press enter to continue\n"); fread(&c, sizeof(c), 1, stdin); /* Cleanup code to free memory allocations on both the client and server. It is interesting that the request to destroy the window is buffered as well. It is not until we request to close the display in this case that the buffer is flushed (meaning that the server will be notified of the destroy window request).
*/ XDestroyWindow(display, window); XCloseDisplay(display); return 0;
}
/* Copyright (c) 2026 Misael Díaz-Maldonado Distributed under the MIT License. See the MIT LICENSE URL https://opensource.org/license/mit for details.
*/
#include <stdio.h>
#include <X11/Xlib.h> int main() {
/* Think of opening the display as connecting to the default session of the X11 server. When you pass zero or NULL to this function it tells the client to lookup the default from your system configuration; more specifically the shell environment variable DISPLAY. This is more portable and less error-prone than typing it yourself. If there is an error the display will be NULL and so the application informs the user about the problem and returns the general purpose error code.
*/ Display *display = XOpenDisplay(NULL); if (!display) { fprintf(stderr, "%s", "failed to open display\n"); return 1; }
/* It is important to keep in mind that in X11 all windows must have a parent window except the root window. So the easiest way to create a window is by using the macro that returns the parent window. You can see that other macros were used to determine dimensions of the window and the color for your screen. This is the most portable way of creating a window with Xlib. As you gain more experience you can use the more advanced version XCreateWindow.
*/ Screen *screen = DefaultScreenOfDisplay(display); Window window = XCreateSimpleWindow( display, DefaultRootWindow(display), 0, 0, WidthOfScreen(screen), HeightOfScreen(screen), 0, BlackPixelOfScreen(screen), BlackPixelOfScreen(screen) ); /* Setting Window Properties and Attributes. Naming the window feels good; knowing that the name is stored on the server-side along with other window properties makes you think of X11 window application development as modifying the state of a state machine. This is good to have in mind because not all Xlib calls involve visible changes on your screen. We need to tell the server that we care about Expose events to be able to know how much we need to wait on the client side for the event to happen, this reveals the asynchronous nature of the X11 windowing system.
*/ XStoreName(display, window, "Handmade Hero"); XSetWindowAttributes template = {}; template.event_mask = ExposureMask; XChangeWindowAttributes(display, window, CWEventMask, &template); /* Here we tell the server to map our game window into our screen but that alone won't make it visible for it is a request that gets buffered locally for performance (client-server roundtrips are expensive).
*/ XMapWindow(display, window); /* Use polling to handle async XWayland communications with the client while retaining backwards compatibility with X11-based Linux desktops. The client waits for the Expose event to happen so that the game window becomes visible. Note that without blocking the client by reading from standard input the game window may not have time to show up before we close the connection to the server. It is recommended to throttle the CPU by sleeping for a suitable time interval at the end of each while-loop cycle; otherwise, many CPU cycles will be wasted on checking window events too frequently.
*/ while (1) { XEvent ev = {}; if (XCheckWindowEvent(display, window, ExposureMask, &ev)) { break; } } char c = 0; fprintf(stdout, "%s", "game paused, press enter to continue\n"); fread(&c, sizeof(c), 1, stdin); /* Cleanup code to free memory allocations on both the client and server. It is interesting that the request to destroy the window is buffered as well. It is not until we request to close the display in this case that the buffer is flushed (meaning that the server will be notified of the destroy window request).
*/ XDestroyWindow(display, window); XCloseDisplay(display); return 0;
}
/* Copyright (c) 2026 Misael Díaz-Maldonado Distributed under the MIT License. See the MIT LICENSE URL https://opensource.org/license/mit for details.
*/
#include <stdio.h>
#include <X11/Xlib.h> int main() {
/* Think of opening the display as connecting to the default session of the X11 server. When you pass zero or NULL to this function it tells the client to lookup the default from your system configuration; more specifically the shell environment variable DISPLAY. This is more portable and less error-prone than typing it yourself. If there is an error the display will be NULL and so the application informs the user about the problem and returns the general purpose error code.
*/ Display *display = XOpenDisplay(NULL); if (!display) { fprintf(stderr, "%s", "failed to open display\n"); return 1; }
/* It is important to keep in mind that in X11 all windows must have a parent window except the root window. So the easiest way to create a window is by using the macro that returns the parent window. You can see that other macros were used to determine dimensions of the window and the color for your screen. This is the most portable way of creating a window with Xlib. As you gain more experience you can use the more advanced version XCreateWindow.
*/ Screen *screen = DefaultScreenOfDisplay(display); Window window = XCreateSimpleWindow( display, DefaultRootWindow(display), 0, 0, WidthOfScreen(screen), HeightOfScreen(screen), 0, BlackPixelOfScreen(screen), BlackPixelOfScreen(screen) ); /* Setting Window Properties and Attributes. Naming the window feels good; knowing that the name is stored on the server-side along with other window properties makes you think of X11 window application development as modifying the state of a state machine. This is good to have in mind because not all Xlib calls involve visible changes on your screen. We need to tell the server that we care about Expose events to be able to know how much we need to wait on the client side for the event to happen, this reveals the asynchronous nature of the X11 windowing system.
*/ XStoreName(display, window, "Handmade Hero"); XSetWindowAttributes template = {}; template.event_mask = ExposureMask; XChangeWindowAttributes(display, window, CWEventMask, &template); /* Here we tell the server to map our game window into our screen but that alone won't make it visible for it is a request that gets buffered locally for performance (client-server roundtrips are expensive).
*/ XMapWindow(display, window); /* Use polling to handle async XWayland communications with the client while retaining backwards compatibility with X11-based Linux desktops. The client waits for the Expose event to happen so that the game window becomes visible. Note that without blocking the client by reading from standard input the game window may not have time to show up before we close the connection to the server. It is recommended to throttle the CPU by sleeping for a suitable time interval at the end of each while-loop cycle; otherwise, many CPU cycles will be wasted on checking window events too frequently.
*/ while (1) { XEvent ev = {}; if (XCheckWindowEvent(display, window, ExposureMask, &ev)) { break; } } char c = 0; fprintf(stdout, "%s", "game paused, press enter to continue\n"); fread(&c, sizeof(c), 1, stdin); /* Cleanup code to free memory allocations on both the client and server. It is interesting that the request to destroy the window is buffered as well. It is not until we request to close the display in this case that the buffer is flushed (meaning that the server will be notified of the destroy window request).
*/ XDestroyWindow(display, window); XCloseDisplay(display); return 0;
}
gcc -std=gnu99 -Wall -g -Og -gdwarf-4 linux_handmade.c -o linux-handmade.bin -lX11
gcc -std=gnu99 -Wall -g -Og -gdwarf-4 linux_handmade.c -o linux-handmade.bin -lX11
gcc -std=gnu99 -Wall -g -Og -gdwarf-4 linux_handmade.c -o linux-handmade.bin -lX11
./linux-handmade.bin
./linux-handmade.bin
./linux-handmade.bin
valgrind -s ./linux-handmade.bin
valgrind -s ./linux-handmade.bin
valgrind -s ./linux-handmade.bin
==17063== Memcheck, a memory error detector
==17063== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==17063== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==17063== Command: ./linux-handmade.bin
==17063== game paused, press enter to continue ==17063== ==17063== HEAP SUMMARY:
==17063== in use at exit: 0 bytes in 0 blocks
==17063== total heap usage: 91 allocs, 91 frees, 100,276 bytes allocated
==17063== ==17063== All heap blocks were freed -- no leaks are possible
==17063== ==17063== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==17063== Memcheck, a memory error detector
==17063== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==17063== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==17063== Command: ./linux-handmade.bin
==17063== game paused, press enter to continue ==17063== ==17063== HEAP SUMMARY:
==17063== in use at exit: 0 bytes in 0 blocks
==17063== total heap usage: 91 allocs, 91 frees, 100,276 bytes allocated
==17063== ==17063== All heap blocks were freed -- no leaks are possible
==17063== ==17063== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==17063== Memcheck, a memory error detector
==17063== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==17063== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==17063== Command: ./linux-handmade.bin
==17063== game paused, press enter to continue ==17063== ==17063== HEAP SUMMARY:
==17063== in use at exit: 0 bytes in 0 blocks
==17063== total heap usage: 91 allocs, 91 frees, 100,276 bytes allocated
==17063== ==17063== All heap blocks were freed -- no leaks are possible
==17063== ==17063== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) - Section 1: Why learning from Handmade Hero still matters
- Section 2: Reasons for creating a GNU/Linux port of Handmade Hero
- Section 3: Is yet another Xlib post necessary?
- Section 4: Why use Xlib for graphics display
- Section 5: Client-Server Architecture
- Section 6: Installing dependencies
- Section 7: Developing an X Client application Subsection 7-A: Headers
Subsection 7-B: Connecting to the XServer
Subsection 7-C: Creating a Window for the Game
Subsection 7-D: Mapping the Window Subsection 7-E: Expose Events Subsection 7-E-1: Handling Expose Events in X11-based Linux Desktops
Subsection 7-E-2: Handling Expose Events in Wayland-based Linux Desktops Subsection 7-F: Pausing the Game Subsection 7-G: Destroying the Window Subsection 7-H: Closing the Display Subsection 7-I: Initial Platform Layer of the Game Subsection 7-J: Compilation Subsection 7-K: Running the Game Subsection 7-L: Checking Memory Leaks with Valgrind
- Subsection 7-A: Headers
- Subsection 7-B: Connecting to the XServer
- Subsection 7-C: Creating a Window for the Game
- Subsection 7-D: Mapping the Window
- Subsection 7-E: Expose Events Subsection 7-E-1: Handling Expose Events in X11-based Linux Desktops
Subsection 7-E-2: Handling Expose Events in Wayland-based Linux Desktops
- Subsection 7-E-1: Handling Expose Events in X11-based Linux Desktops
- Subsection 7-E-2: Handling Expose Events in Wayland-based Linux Desktops
- Subsection 7-F: Pausing the Game
- Subsection 7-G: Destroying the Window
- Subsection 7-H: Closing the Display
- Subsection 7-I: Initial Platform Layer of the Game
- Subsection 7-J: Compilation
- Subsection 7-K: Running the Game
- Subsection 7-L: Checking Memory Leaks with Valgrind
- Section 8: Conclusions
- Section 9: Final Thoughts
- Section 10: References
- Section 11: Credits
- Section 12: Ports - Subsection 7-A: Headers
- Subsection 7-B: Connecting to the XServer
- Subsection 7-C: Creating a Window for the Game
- Subsection 7-D: Mapping the Window
- Subsection 7-E: Expose Events Subsection 7-E-1: Handling Expose Events in X11-based Linux Desktops
Subsection 7-E-2: Handling Expose Events in Wayland-based Linux Desktops
- Subsection 7-E-1: Handling Expose Events in X11-based Linux Desktops
- Subsection 7-E-2: Handling Expose Events in Wayland-based Linux Desktops
- Subsection 7-F: Pausing the Game
- Subsection 7-G: Destroying the Window
- Subsection 7-H: Closing the Display
- Subsection 7-I: Initial Platform Layer of the Game
- Subsection 7-J: Compilation
- Subsection 7-K: Running the Game
- Subsection 7-L: Checking Memory Leaks with Valgrind - Subsection 7-E-1: Handling Expose Events in X11-based Linux Desktops
- Subsection 7-E-2: Handling Expose Events in Wayland-based Linux Desktops - libx11-dev package provides the client interface to Xlib
- libx11-doc provides the official Xlib documentation as man pages - We have an initial idea of what the platform layer of the game (for graphics display) with Xlib might look like.
- In the process we learned that Xlib has a client-server architecture that enables multiple client applications to draw to the screen (a shared resource) concurrently; it is the server that processes and resolves those requests to -weight: 500;">update the screen's framebuffer.
- We also found out that in Xlib the display refers to the connection to the X Server which manages the screens (for graphics output) along with the peripherals such as the keyboard, mouse, or game console controller (for user input).
- We have realized that Xlib provides convenient macros for getting at the screen, visuals, etc. in a portable way. We have found that under the hood these macros cast the "opaque" display structure into a known type (private display) and subsequently dereferenced to obtain, for example, the root window ID, screen dimensions, or the black pixel value for the screen. These macros -weight: 500;">enable developers to change Xlib internals while not breaking the existing client code.
- Additionally, we learned that Xlib allocates the resources for the window on the server side and that the client code (our game) allocates the window resource Id (or handle).
- We used the GNU debugger gdb to demonstrate that client applications batch their requests into an output buffer.
- We also saw that Xlib commands that need synchronization such as XWindowEvent flush the output buffer and block until the server responds.
- To verify that our code has no memory leaks we used valgrind's memcheck tool.
- In the end we succeeded in making our game window visible and ready to put graphics on it. - Xlib - C Language Interface The official Xorg Technical Specification of the X11 Protocol with the C bindings.
- The X New Developer's Guide Provides a high-level documentation of the X11 Protocol that is tailored for new contributors. It provides information about modern extensions, the asynchronous X C Bindings (XCB), a fresher presentation of the original C Language Interface libX11, and contributing guidelines.
- Xlib Repository The official Xlib repository hosted in GitLab. This is the definitive Xlib documentation written in source code form.
- X11 Protocol Headers Repository The official GitLab repository hosting the X11 protocol headers. Some of the X11 protocol definitions were referenced from the standard X11 protocol headers.
- Tronche's Xlib - C Language Interface This is the legacy (release 6 of the) X11 protocol C Language Interface by Christophe Tronche. It is worth mentioning that Tronche brought the Xlib documentation to the web before the X Consortium and this is why it is still a relevant, highly indexed, and searchable resource.
- Handmade Network Tour through Xlib and related technologies A practical tour of Xlib (creating the window, handling events, and working with the framebuffer) to get you on the fast track for developing the Handmade Hero game in Linux written by Florian Behr. - Xlib Learning Notes by Faison Zutavern. A series of study notes that covers topics of interest for developers just getting started with Xlib, such as opening a window, toggling fullscreen mode, and using OpenGL. This was the first GitHub repository that I found on Xlib when I was exploring the Quake engine. I am citing this work as a token of respect.
- Xlib Examples A repository of Xlib examples by Charles Qiu. This is another repository that I found useful when studying the Quake engine. I am sharing it as well as a token of respect. - First Linux port of Handmade Hero A series by David Gow on developing the platform layer for Handmade Penguin (a Linux port of Handmade Hero). David leverages the Simple Direct Media Layer (SDL) for handling graphics, sounds, and user input. This is useful for people that want to defer the hardships of implementing the platform layer with Xlib and ALSA.
- Multi-Platform Handmade Hero A comprehensive implementation of Handmade Hero on various platforms by Laszlo Korte. He has succeeded in porting the game to MacOS, iOS, Android, and Linux (both Wayland and X11). His work demonstrates that a well-designed game layer written in one platform can be easily ported to other platforms.