Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b67cbfb
fix(edbbd2f): missing `{`
h8d13 Aug 1, 2026
142a655
feat(core): add optional `bg_color` keys
h8d13 Aug 1, 2026
c3eb151
fix(reload): + show working example in docs
h8d13 Aug 2, 2026
57ca706
chore(cfg): clean-up dead left-over stray keys from bace32d
h8d13 Aug 2, 2026
2dfba1e
chore(build): add missing `dmenu` to opt
h8d13 Aug 2, 2026
009b639
Add hint in readme for hot-reload
h8d13 Aug 2, 2026
8711e81
correct usage
h8d13 Aug 2, 2026
ce2e854
chore(cfg): remove one more dead key
h8d13 Aug 2, 2026
2367be9
chore(build): add `make package` verb for Arch builds
h8d13 Aug 2, 2026
85078ae
chore(build): add `-xset` to opt depends
h8d13 Aug 3, 2026
2f5b075
add example to docs, never go idle
h8d13 Aug 3, 2026
4f9a927
correct usage
h8d13 Aug 3, 2026
cba1b05
chore(build): correct `picom` description
h8d13 Aug 4, 2026
1b1b497
chore(docs): add hint to close
h8d13 Aug 4, 2026
b824bab
fix(ewmh): set _NET_ACTIVE_WINDOW on root, publish WM_STATE
h8d13 Aug 4, 2026
6d06300
fix(core): clang-tidy warning use integer ms for motion throttle time…
h8d13 Aug 4, 2026
1b76a76
fix(desktops): wrap cycledesktopdown to the last slot
h8d13 Aug 4, 2026
35b7ee3
explicit cast
h8d13 Aug 4, 2026
19e8d52
fix(keycallbacks): make header self-contained, narrow explicitly
h8d13 Aug 4, 2026
41c5335
fix(log): popen leak and fork-per-line + default to off for release
h8d13 Aug 4, 2026
b44caf4
chore(clean): remove mentions of cmdoutput now that we use strftime
h8d13 Aug 4, 2026
66e0296
chore(clean): nuke .desktop file, not intended flow.
h8d13 Aug 4, 2026
dfc64de
fix(cache)
h8d13 Aug 4, 2026
5864872
chore(cfg): simplify loading/logging + spacing in default
h8d13 Aug 4, 2026
1ac0df5
rem
h8d13 Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
*.o
*.a
*.patch
*.data
.*.md
**.tar.zst
.cache/
bin/
pkg/
/ragnar/
**.tar.zst
src/ragnar/
.gdb.log
compile_commands.json
compile_flags.txt
.*.md
38 changes: 35 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ CC = cc
# bought nothing and -ffinite-math-only would let divisions by a zero-sized
# monitor fold away instead of showing up.
CFLAGS ?= -O2
ALL_CFLAGS = $(CFLAGS) $(CPPFLAGS) -Wall -Wextra -pedantic -isystem api/include
WARN_CFLAGS = -Wall -Wextra -pedantic -isystem api/include
ALL_CFLAGS = $(CFLAGS) $(CPPFLAGS) $(WARN_CFLAGS)

# -O1 : for readable ASAN traces. Separate from CFLAGS because that is ?=
# and a distro build must not pick these up.
DEBUG_CFLAGS = -O1 -g -fno-omit-frame-pointer -fsanitize=address,undefined
ALL_DEBUG_CFLAGS = $(DEBUG_CFLAGS) $(CPPFLAGS) $(WARN_CFLAGS)

LDLIBS = -lxcb -lxcb-keysyms -lxcb-icccm -lxcb-cursor -lxcb-randr -lxcb-xfixes -lX11 -lX11-xcb -lconfig

Expand All @@ -20,6 +26,16 @@ all:
mkdir -p ./bin
$(CC) -o bin/$(BIN) $(ALL_CFLAGS) $(LDFLAGS) $(SRC) $(LDLIBS)

# ASan + UBSan + LSan build, overwrites bin/ragnar. LSan only reports on a
# normal exit, so quit ragnar through its own keybind: a SIGKILL prints
# nothing. .PHONY matters here, the ./debug Xephyr script shares the name
# and make would otherwise call the target up to date.
# make debug && ./debug
.PHONY: debug
debug:
mkdir -p ./bin
$(CC) -o bin/$(BIN) $(ALL_DEBUG_CFLAGS) $(LDFLAGS) $(SRC) $(LDLIBS)

# client-side IPC library. the WM links none of it, only the headers under
# api/include are needed to build. opt-in, for writing external clients.
.PHONY: api
Expand All @@ -37,15 +53,31 @@ install-api:
.PHONY: install
install:
install -Dm755 bin/$(BIN) -t $(DESTDIR)$(BINDIR)
install -Dm644 ragnar.desktop -t $(DESTDIR)$(PREFIX)/share/xsessions
install -Dm644 cfg/ragnar.cfg -t $(DESTDIR)$(SYSCONFDIR)/ragnarwm

# the shipped cfg is the only fallback a fresh install has: readconfig
# terminates when neither the user nor the global path parses, so a syntax
# error here means a fresh install cannot start at all.
.PHONY: check
check:
mkdir -p ./bin
$(CC) -o bin/config_check $(ALL_CFLAGS) $(LDFLAGS) cfg/config_check.c -lconfig
./bin/config_check cfg/ragnar.cfg

# makepkg's $srcdir defaults to ./src, which collides with this project's
# own src/. build out of tree so the clone never lands in the worktree.
BUILDDIR ?= /tmp/makepkg
SRCDEST ?= $(HOME)/.cache/makepkg/sources

.PHONY: package
package:
BUILDDIR=$(BUILDDIR) SRCDEST=$(SRCDEST) makepkg -f

.PHONY: clean
clean:
$(RM) bin/*

.PHONY: uninstall
uninstall:
$(RM) $(DESTDIR)$(BINDIR)/$(BIN)
$(RM) $(DESTDIR)$(PREFIX)/share/xsessions/ragnar.desktop
$(RM) -r $(DESTDIR)$(SYSCONFDIR)/ragnarwm
13 changes: 12 additions & 1 deletion PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@ depends=(
)
makedepends=('git' 'make' 'gcc')
optdepends=(
'picom: for proper vsync/fix tearing'
'picom: additional compositor features'
'dmenu: default launcher'
'alacritty: default terminal keybind'
'polybar: optional status/desktops bars'
'feh: optional set wallpaper for X displays'
'ttf-dejavu: fonts for both the above'
'wireplumber: default volume keybinds'
'brightnessctl: default brightness keybinds'
'playerctl: default media player keybinds'
'xorg-xset: for screenblanking idle/DPMS'
)

provides=('ragnarwm')
options=('!debug')
backup=('etc/ragnarwm/ragnar.cfg')
Expand All @@ -43,6 +47,7 @@ install="${pkgname}.install"
source=("${_pkgname}::git+${url}.git#branch=${_branch}")
sha256sums=('SKIP')

# TODO add a release flow that bumps on gh automatically
pkgver() {
cd $_pkgname || exit 1
echo $pkgver
Expand All @@ -53,6 +58,12 @@ build() {
make
}

# check the shipped config is not borken
check() {
cd $_pkgname || exit 1
make check
}

package() {
cd $_pkgname || exit 1
# single source of truth: same install target ./install.sh drives.
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,18 @@ Ragnar uses `libconfig` to load an external configuration file:
~/.config/ragnarwm/ragnar.cfg
```

The configuration is loaded on startup and can be reloaded while the window manager is running, typically through a keybinding.
The configuration is loaded on startup and can be reloaded while the window manager is running, typically through a keybinding (Super+C).

Example contents of `.xinitrc`:

```console
feh --bg-fill ~/Downloads/nerd.jpg;
xset s off -dpms;
picom -b; polybar &
exec ragnar
```

Then simply; `startx`
Then simply; `startx` (and `pkill xinit` to stop it.)

> By default it uses `alacritty` (Super+Return) and `dmenu` (Super+S), if you haven't edited these yet.
> Both of these need fonts file; for instance `ttf-dejavu`
Expand Down
4 changes: 2 additions & 2 deletions api/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ clientinit(socket_client_t* cl) {

memset(&cl->addr, 0, sizeof(struct sockaddr_un));
cl->addr.sun_family = AF_UNIX;
strncpy(cl->addr.sun_path, SOCKPATH, sizeof(cl->addr.sun_path) - 1);
rg_socket_path(cl->addr.sun_path, sizeof(cl->addr.sun_path));

if(s_logging) {
printf("ragnar api: created unix domain socket on: '%s'\n", SOCKPATH);
printf("ragnar api: created unix domain socket on: '%s'\n", cl->addr.sun_path);
}
return 0;
}
Expand Down
30 changes: 30 additions & 0 deletions api/include/ragnar/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

/*
* IPC socket path, computed rather than fixed. XDG_RUNTIME_DIR scopes it
* per user and DISPLAY per X server: one hardcoded path meant a second
* instance (a nested Xephyr session, a second seat) unlinked the running
* WM's socket out from under it at startup
*
* static inline in the public header on purpose. The WM links none of
* api.c and only includes these headers, so this is the single place both
* sides can share the path and not drift apart
*/
static inline void
rg_socket_path(char* buf, size_t len) {
const char* dir = getenv("XDG_RUNTIME_DIR");
if(!dir) {
dir = "/tmp";
}
const char* display = getenv("DISPLAY");
if(!display) {
display = ":0";
}
// DISPLAY is ":1" or ":0.0"; drop the leading colon so the result reads
// as ragnar-1.socket rather than ragnar-:1.socket
if(*display == ':') {
display++;
}
snprintf(buf, len, "%s/ragnar-%s.socket", dir, display);
}

typedef int32_t RgWindow;

Expand Down
21 changes: 21 additions & 0 deletions cfg/config_check.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <libconfig.h>
#include <stdio.h>

int main(int argc, char** argv) {
if(argc != 2) {
fprintf(stderr, "usage: %s <config file>\n", argv[0]);
return 2;
}

config_t cfg;
config_init(&cfg);
if(config_read_file(&cfg, argv[1])) {
config_destroy(&cfg);
printf("sample config is valid.");
return 0;
}

fprintf(stderr, "%s:%d: %s\n", argv[1], config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
return 1;
}
49 changes: 23 additions & 26 deletions cfg/ragnar.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
# - setfloatingmode
# - updatebarslayout
# - cycledownlayout
# - cycleuplayout
# - addmasterlayout
# - removemasterlayout
# - incmasterarealayout
Expand All @@ -73,33 +74,37 @@
# - cyclefocusmonitordown
# - cyclefocusmonitorup
# - togglescratchpad
# - reloadconfigfile
#
# ----------------------
# NOTE: For all key options, see the functions at:
# https://github.com/cococry/ragnar/blob/main/src/structs.h#L17
# ----------------------
#

# Specifies the width of the border around client
# windows
win_border_width = 2;

# Specifies the color of the border around client
# windows
win_border_color = 0x3B3B3B;

# Specifies the color of the border around
# selected/focused client windows
win_border_color_selected = 0xE6DFDC;

# Specifies the main modifier key that is
# used to execute window manager shortcuts
mod_key = "Super";

# Specifies the modifier key that is used
# to interact with clients windows
win_mod = "Super";

# Specfies the mouse button that needs to be
# held in order to move client windows
move_button = "LeftMouse";

# Specfies the mouse button that needs to be
# held in order to resize client windows
resize_button = "RightMouse";
Expand All @@ -113,47 +118,30 @@ resize_button = "RightMouse";
# Specifies the desktop index that is initially
# selected on every monitor
initial_desktop = 0;

# Specfies the number of allocated virtual
# desktops
num_desktops = 9;

# Specifies the name of every virtual desktop
# in order.
desktop_names = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];

# Specifies whether or not server-side window
# decorations should be enabled.
use_decoration = false;
# Specifies whether or not server-side titlebars
# should be shown on startup. (Ignored when
# use_decoration is set to false.)
show_titlebars_init = false;

# Specifies the height (in pixels) of the
# titlebar of client windows.
titlebar_height = 30
# Specifies the color of titlebars of
# client windows
titlebar_color = 0xffffff;

# Specifies the color of the font that is used
# across the window manager's UI
font_color = 0xff0000ff;
# Specifies the path to the font file to use as
# the window manager's font
font_path = "/usr/share/fonts/TTF/JetBrainsMonoNerdFont-Bold.ttf";

# Specifies the area that the master window takes
# up in master-slave layouts initially.
# (in 0.0-1.0 %)
layout_master_area = 0.5;

# Specifies the minimum area that master windows
# need to take up in master-slave layouts
# (in 0.0-1.0 %)
layout_master_area_min = 0.1;

# Specifies the maximum a2rea that master windows
# can take up in master-slave layouts
# (in 0.0-1.0 %)
layout_master_area_max = 0.9;

# Specifies the amount that the master area changes/steps
# when it is decreased/increased.
# (in 0.0-1.0 %)
Expand All @@ -162,6 +150,7 @@ layout_master_area_step = 0.1;
# Specifies the amount that areas of windows
# within layouts change when they are increased/decreased
# (in px)

layout_size_step = 100.0;
# Specifies the minimum area that windows within
# layouts need to take up
Expand All @@ -176,9 +165,11 @@ key_win_move_step = 100.0;
# Specifies the initial gap between windows
# within layouts (in px)
win_layout_gap = 5;

# Specifies the maximum gap that windows within
# layouts can have around each other (in px)
win_layout_gap_max = 150;

# Specifies the amount that the gap between
# non-floating windows changes when it's
# decreased/increased (in px)
Expand Down Expand Up @@ -208,15 +199,18 @@ max_struts = 8;
# Specifies the cursor image to use for the root window
cursor_image = "arrow";

# Logging (log file is ~/.ragnarwm.log)
# Specifies the colour the root window is painted with.
# Optional: when unset does nothing. (Let ex: `feh` take-over)
# bg_color = 0x1A1A1A;

# Specifies whether or not to log messages
# to the console
log_messages = true;
log_messages = false;

# Specifies whether or not to log messages
# to the log file
should_log_to_file = true;
# builds it as $HOME/.ragnarwm.log
should_log_to_file = false;

# Specifies the maximum number of scratchpads
# that can be allocated
Expand All @@ -226,6 +220,8 @@ max_scratchpads = 10;

# =========== Key bindings ===========

# four sub-keys per entry: mod (optional), key, do, and then cmd / i as the
# passthrough payload. Any other sub-key here is ignored silently.
keybinds = (
{
mod = "Super";
Expand Down Expand Up @@ -550,6 +546,7 @@ keybinds = (
key = "KeyC";
do = "reloadconfigfile";
},
{
key = "KeyAudioLowerVolume";
do = "runcmd";
cmd = "wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%-";
Expand Down
7 changes: 0 additions & 7 deletions ragnar.desktop

This file was deleted.

Loading