Skip to main content
Fern uses X11 for native rendering on Linux. The Linux renderer provides full support for window management, event handling, and pixel-perfect rendering.

Prerequisites

Required Dependencies

The Linux build requires X11 development libraries:
sudo apt-get install libx11-dev libfontconfig1-dev libfreetype6-dev

Build Tools

Ensure you have CMake and a C++17 compiler:
sudo apt-get install cmake g++ pkg-config

Building for Linux

Using the Build Script

The quickest way to build for Linux:
build.sh
./build.sh linux
Build a specific example:
./build.sh linux platform_test
Build in debug mode:
./build.sh --debug linux

Using CMake Directly

mkdir build && cd build
cmake ..
cmake --build .

Linux Renderer Implementation

The Linux renderer (linux_renderer.cpp) implements the PlatformRenderer interface using X11.

Key Features

Creates a native X11 window with support for:
  • Window resizing
  • Close events (WM_DELETE_WINDOW)
  • Focus management (WM_TAKE_FOCUS)
  • Window title updates
Uses XImage for direct pixel buffer manipulation:
linux_renderer.cpp:316-325
ximage_ = XCreateImage(
    display_, DefaultVisual(display_, screen), DefaultDepth(display_, screen),
    ZPixmap, 0, (char*)pixelBuffer_, width_, height_, 32, 0
);
Supports complex text input using X Input Method (XIM):
linux_renderer.cpp:122-134
xim_ = XOpenIM(display_, nullptr, nullptr, nullptr);
xic_ = XCreateIC(xim_,
                XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
                XNClientWindow, window_,
                XNFocusWindow, window_,
                nullptr);
Processes X11 events including:
  • Mouse motion and clicks
  • Keyboard input (key presses, text input)
  • Window resize and configuration
  • Focus changes

Compilation Flags

The build script uses these flags for Linux:
build.sh:139-146
LINUX_FLAGS="-std=c++17 -D__linux__ -I$SRC_DIR/include"
LINUX_LIBS="-lX11"

if [[ "$BUILD_TYPE" == "Debug" ]]; then
    LINUX_FLAGS="$LINUX_FLAGS -g -O0 -DDEBUG"
else
    LINUX_FLAGS="$LINUX_FLAGS -O3 -DNDEBUG"
fi

CMake Configuration

CMake automatically finds X11 libraries:
CMakeLists.txt:84-98
elseif(UNIX AND NOT APPLE)
    find_package(X11 REQUIRED)
    target_link_libraries(fern PUBLIC ${X11_LIBRARIES})
    target_include_directories(fern PUBLIC ${X11_INCLUDE_DIR})
    
    # Find fontconfig and freetype on Linux
    find_package(PkgConfig REQUIRED)
    pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
    pkg_check_modules(FREETYPE REQUIRED freetype2)
    
    target_link_libraries(fern PUBLIC ${FONTCONFIG_LIBRARIES} ${FREETYPE_LIBRARIES})
    target_include_directories(fern PUBLIC ${FONTCONFIG_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS})
    target_compile_options(fern PUBLIC ${FONTCONFIG_CFLAGS_OTHER} ${FREETYPE_CFLAGS_OTHER})
endif()

Key Code Translation

The Linux renderer translates X11 KeySyms to Fern’s cross-platform KeyCode enum:
linux_renderer.cpp:34-83
KeyCode translateXKeyToFernKey(KeySym keysym) {
    switch (keysym) {
        case XK_Return: return KeyCode::Enter;
        case XK_Escape: return KeyCode::Escape;
        case XK_space: return KeyCode::Space;
        case XK_BackSpace: return KeyCode::Backspace;
        case XK_Tab: return KeyCode::Tab;
        case XK_Left: return KeyCode::ArrowLeft;
        case XK_Right: return KeyCode::ArrowRight;
        case XK_Up: return KeyCode::ArrowUp;
        case XK_Down: return KeyCode::ArrowDown;
        case XK_a: case XK_A: return KeyCode::A;
        // ... (full alphabet and numbers)
        default: return KeyCode::Unknown;
    }
}

Running Linux Applications

After building, run the executable:
./build/linux/platform_test
Make sure your DISPLAY environment variable is set. If running over SSH, use X forwarding:
ssh -X user@host

Troubleshooting

Error: Cannot connect to X server. Is DISPLAY set?Solution: Ensure DISPLAY is set:
echo $DISPLAY
export DISPLAY=:0
Error: X11/Xlib.h: No such file or directorySolution: Install X11 development packages:
sudo apt-get install libx11-dev
Error: Font-related compilation or runtime errorsSolution: Install fontconfig and freetype:
sudo apt-get install libfontconfig1-dev libfreetype6-dev

Performance Considerations

Pixel Buffer Updates

The renderer uses memcpy for efficient pixel buffer transfers:
linux_renderer.cpp:174
memcpy(pixelBuffer_, pixelBuffer, bufferSize);

Event Processing

Events are processed in batches during pollEvents():
linux_renderer.cpp:196-198
while (XPending(display_) > 0) {
    XNextEvent(display_, &event);
    // Process event...
}

Platform Detection at Runtime

Your application can query the platform at runtime:
auto renderer = Fern::createRenderer();
std::cout << renderer->getPlatformName() << std::endl;
// Output: "Linux (X11)"

Next Steps

Web/WASM

Compile the same code to WebAssembly

Platform Overview

Learn about cross-platform architecture

Build docs developers (and LLMs) love