Architecture
Technical documentation for developers and contributors.
Overview
DWN is written in ANSI C (C99) and follows a modular single-responsibility architecture.
A global DWNState singleton manages all state, and the main event loop
dispatches X11 events to specialized modules.
Project Statistics
Directory Structure
dwn/
├── src/ # Source files (.c)
│ ├── main.c # Entry point, event loop
│ ├── client.c # Window management
│ ├── workspace.c # Virtual desktops
│ ├── layout.c # Tiling algorithms
│ ├── decorations.c # Title bars, borders
│ ├── panel.c # Top/bottom panels
│ ├── systray.c # System tray widgets
│ ├── notifications.c # D-Bus notifications
│ ├── atoms.c # X11 atoms (EWMH/ICCCM)
│ ├── keys.c # Keyboard handling
│ ├── config.c # INI parser
│ ├── ai.c # AI integration
│ └── util.c # Utilities
├── include/ # Header files (.h)
├── site/ # Documentation website
├── Makefile # Build system
├── CLAUDE.md # AI assistant context
└── README.md # Project readme
Core Modules
| Module | File | Responsibility |
|---|---|---|
| Main | main.c |
X11 initialization, event loop, signal handling, module orchestration |
| Client | client.c |
Window lifecycle, focus management, frame creation, client list |
| Workspace | workspace.c |
9 virtual desktops, per-workspace state, window assignment |
| Layout | layout.c |
Tiling (master+stack), floating, monocle layout algorithms |
| Decorations | decorations.c |
Window title bars, borders, decoration rendering |
| Panel | panel.c |
Top panel (taskbar, workspace indicators), bottom panel (clock) |
| Systray | systray.c |
System tray with WiFi/audio/battery indicators, dropdowns |
| Notifications | notifications.c |
D-Bus notification daemon (org.freedesktop.Notifications) |
| Atoms | atoms.c |
X11 EWMH/ICCCM atom management and property handling |
| Keys | keys.c |
Keyboard shortcut capture, keybinding registry, callbacks |
| Config | config.c |
INI-style config loading and parsing |
| AI | ai.c |
Async OpenRouter API integration, Exa semantic search |
| Autostart | autostart.c |
XDG Autostart support, .desktop file parsing, concurrent app launch |
| Util | util.c |
Logging, memory allocation, string utilities, file helpers |
Module Dependencies
main.c (orchestrator)
├── client.c
│ ├── decorations.c
│ ├── config.c
│ └── atoms.c
├── workspace.c
│ ├── client.c
│ ├── layout.c
│ └── atoms.c
├── panel.c
│ ├── client.c
│ └── config.c
├── systray.c
│ └── config.c
├── notifications.c (independent)
├── ai.c (independent)
├── autostart.c
│ └── config.c
└── keys.c
└── config.c
Global State (DWNState)
All window manager state is centralized in a single DWNState structure.
This simplifies state management and makes the codebase easier to understand.
typedef struct {
Display *display; // X11 connection
Window root; // Root window
int screen; // Default screen
Client *clients[MAX_CLIENTS]; // All managed windows
int client_count;
Workspace workspaces[MAX_WORKSPACES]; // Virtual desktops
int current_workspace;
Panel top_panel;
Panel bottom_panel;
Config config; // User configuration
KeyBinding keys[MAX_KEYBINDINGS];
// EWMH atoms
Atom atoms[ATOM_COUNT];
} DWNState;
extern DWNState *dwn; // Global singleton
Event Loop
DWN uses a traditional X11 event loop with XNextEvent. Events are dispatched to appropriate handlers based on type.
int main(int argc, char *argv[]) {
dwn_init(); // Initialize X11, atoms, config
setup_keybindings(); // Register keyboard shortcuts
setup_panels(); // Create panel windows
XEvent event;
while (running) {
XNextEvent(dwn->display, &event);
switch (event.type) {
case MapRequest:
handle_map_request(&event.xmaprequest);
break;
case UnmapNotify:
handle_unmap_notify(&event.xunmap);
break;
case KeyPress:
handle_key_press(&event.xkey);
break;
case ButtonPress:
handle_button_press(&event.xbutton);
break;
case ConfigureRequest:
handle_configure_request(&event.xconfigurerequest);
break;
// ... more event types
}
}
dwn_cleanup();
return 0;
}
Key Constants
| Constant | Value | Purpose |
|---|---|---|
MAX_CLIENTS |
256 | Maximum managed windows |
MAX_WORKSPACES |
9 | Number of virtual desktops |
MAX_MONITORS |
8 | Multi-monitor support limit |
MAX_NOTIFICATIONS |
32 | Concurrent notifications |
MAX_KEYBINDINGS |
64 | Registered keyboard shortcuts |
Coding Conventions
Naming
snake_casefor functions and variablesCamelCasefor types and structs- Module prefix for functions (e.g.,
client_focus()) - Constants in
UPPER_SNAKE_CASE
Style
- 4-space indentation
- K&R brace style
- Max 100 characters per line
- clang-format for consistency
void client_focus(Client *c) {
if (!c) return;
// Unfocus previous
if (dwn->focused && dwn->focused != c) {
client_unfocus(dwn->focused);
}
dwn->focused = c;
XSetInputFocus(dwn->display, c->window, RevertToPointerRoot, CurrentTime);
XRaiseWindow(dwn->display, c->frame);
decorations_update(c);
atoms_set_active_window(c->window);
}
EWMH/ICCCM Support
DWN implements key Extended Window Manager Hints and ICCCM protocols for compatibility with modern applications.
EWMH Atoms
- _NET_SUPPORTED
- _NET_CLIENT_LIST
- _NET_CLIENT_LIST_STACKING
- _NET_ACTIVE_WINDOW
- _NET_CURRENT_DESKTOP
- _NET_NUMBER_OF_DESKTOPS
- _NET_WM_STATE
- _NET_WM_STATE_FULLSCREEN
- _NET_WM_STATE_MAXIMIZED_*
- _NET_WM_WINDOW_TYPE
- _NET_WM_NAME
ICCCM Support
- WM_STATE
- WM_PROTOCOLS
- WM_DELETE_WINDOW
- WM_TAKE_FOCUS
- WM_NORMAL_HINTS
- WM_SIZE_HINTS
- WM_CLASS
- WM_NAME
- WM_TRANSIENT_FOR
Build System
DWN uses a simple Makefile-based build system with pkg-config for dependency detection.
| Target | Command | Description |
|---|---|---|
| Build (release) | make |
Optimized build with -O2 |
| Build (debug) | make debug |
Debug symbols, -DDEBUG flag |
| Install | sudo make install |
Install to PREFIX (/usr/local) |
| Clean | make clean |
Remove build artifacts |
| Format | make format |
Run clang-format on sources |
| Check | make check |
Run cppcheck static analysis |
| Test | make run |
Run in Xephyr nested server |
| Dependencies | make deps |
Auto-install for your distro |
Contributing
Contributions are welcome! Here's how to get started:
Fork & Clone
Fork the repository and clone your fork locally.
Create a Branch
Create a feature branch: git checkout -b feature/my-feature
Make Changes
Follow coding conventions. Run make format and make check.
Test
Test your changes with make run in a nested X server.
Submit PR
Push your branch and open a pull request with a clear description.