NWM - A Scrollable Tiling Window Manager
Table of Contents
- Introduction
- Philosophy and Goals
- Installation
- Getting Started
- Understanding Layouts
- Configuration
- Keybindings
- Window Management
- Workspace Management
- The Status Bar
- System Tray
- Advanced Configuration
- Troubleshooting
- Contributing
- Appendix
- Glossary
- Comparison with Other Window Managers
- Useful Resources
- Changelog
- FAQ (Quick Reference)
- How do I change the terminal?
- How do I change the modifier key?
- How do I add more workspaces?
- Can I use NWM on Wayland?
- Why no multi-monitor support?
- How do I set a wallpaper?
- Why do I need to recompile for configuration changes?
- Is NWM suitable for beginners?
- Where can I find example configurations?
- Contact
Introduction
NWM (short for "No Window Manager" or "New Window Manager") is a tiling window manager for the X Window System. It is written in modern C++14 and provides both traditional master-stack tiling and a unique horizontal scrolling layout.
Unlike traditional window managers that require patching to add features, NWM follows a "configuration through compilation" approach where you edit the source code directly and rebuild. The codebase is intentionally kept clean and modular to make modifications straightforward.
Demo
What is a Tiling Window Manager?
A tiling window manager is a window manager with an organization of the screen into mutually non-overlapping frames, as opposed to the more common approach of coordinate-based stacking of overlapping objects (windows) that tries to fully emulate the desktop metaphor.
In simpler terms: instead of windows floating around and overlapping each other, a tiling window manager automatically arranges windows to use all available screen space without overlaps.
Why NWM?
NWM was created as a hobby project to explore what a tiling window manager could be if:
- It had a clean, modern codebase using C++14
- It didn't rely on patches for customization
- It offered a unique "scrollable" layout alongside traditional tiling
- It came with batteries included (status bar, system tray) but remained lightweight
Philosophy and Goals
Clean Code Over Patches
Many suckless-inspired projects use a patch system where features are added by applying diffs to the source code. This can lead to:
- Patch conflicts
- Difficulty maintaining multiple patches
- Opaque code changes
NWM instead provides a clean, well-structured codebase where you can directly see and modify what you need. The configuration file (src/config.hpp) is actual C++ code, giving you the full power of the language for configuration.
Daily Drivable Defaults
The default configuration is meant to be usable immediately. You shouldn't need to spend hours configuring before you can use the window manager. The defaults include:
- Sensible keybindings (Super/Windows key as modifier)
- Built-in status bar with system information
- System tray support
- 9 workspaces
- Both traditional tiling and scrollable layouts
Scrollable First
The unique feature of NWM is its horizontal scrolling layout. While most tiling window managers focus on vertical or grid layouts, NWM offers a side-by-side arrangement where you scroll through windows horizontally. This is particularly useful for:
- Comparing files side-by-side
- Working with many terminals simultaneously
- Tasks that benefit from seeing two things at once
Extensible Through Source
Since configuration is done through C++ code, you have complete control over the window manager's behavior. Want to add a new keybinding? Edit the array. Want to change how windows are tiled? Modify the tiling functions. The entire source is available and documented.
Installation
Dependencies
NWM requires the following libraries to build and run:
Required Libraries
- X11 (libX11): Core X Window System library
- Xft (libXft): X FreeType library for font rendering
- FreeType2 (libfreetype): Font rendering engine
- Fontconfig (libfontconfig): Font configuration and customization library
- Xrender (libXrender): X Rendering Extension library
Build Tools
- C++ compiler with C++14 support (GCC 5+ or Clang 3.4+)
- GNU Make
Installing Dependencies
Arch Linux
sudo pacman -S base-devel xorg-server libx11 libxft freetype2 fontconfig libxrender
Debian/Ubuntu
sudo apt install build-essential xorg libx11-dev libxft-dev libfreetype6-dev libfontconfig1-dev libxrender-dev
Fedora
sudo dnf install @development-tools xorg-x11-server-Xorg libX11-devel libXft-devel freetype-devel fontconfig-devel libXrender-devel
Gentoo
emerge --ask x11-base/xorg-server x11-libs/libX11 x11-libs/libXft media-libs/freetype media-libs/fontconfig x11-libs/libXrender
Void Linux
sudo xbps-install -S base-devel xorg libX11-devel libXft-devel freetype-devel fontconfig-devel libXrender-devel
Building from Source
Cloning the Repository
First, clone the NWM repository from GitHub:
git clone https://github.com/xsoder/nwm.git cd nwm
Understanding the Build System
NWM uses a simple Makefile for building. The Makefile includes:
- Compiler flags for optimization (
-O3) and warnings (-Wall -Wextra) - Proper linking of required libraries
- Installation targets for the binary and desktop entry
You can examine the Makefile to understand exactly what's being compiled and how.
Compiling
To compile NWM:
make
This will:
- Compile each source file (
src/nwm.cpp,src/bar.cpp,src/tiling.cpp,src/systray.cpp) into object files - Link all object files together with the required libraries
- Produce the
nwmbinary in the current directory
Installing System-Wide
To install NWM system-wide (requires root privileges):
sudo make install
This will:
- Install the
nwmbinary to/usr/local/bin/nwm - Install the desktop entry to
/usr/share/xsessions/nwm.desktop
The desktop entry allows display managers (like LightDM, GDM, SDDM) to show NWM as a session option at login.
Custom Installation Prefix
If you want to install to a different location:
make PREFIX=/custom/path install
For example, to install to your home directory:
make PREFIX=$HOME/.local install
Cleaning Build Files
To remove compiled object files and the binary:
make clean
Uninstalling
To remove NWM from your system:
sudo make uninstall
Nix/NixOS Installation
NWM includes a flake.nix for Nix users.
Building with Nix
nix build
Running with Nix
nix run
Development Shell
To enter a development environment with all dependencies:
nix develop
This provides a shell with all build tools, libraries, and useful utilities pre-installed.
Getting Started
Starting NWM
There are several ways to start NWM, depending on your setup.
Using a Display Manager (Recommended)
If you use a display manager (LightDM, GDM, SDDM, etc.), NWM will appear in the session list after installation. Simply:
- Log out or restart
- At the login screen, look for a session selector (usually a gear icon or dropdown menu)
- Select "NWM" from the list
- Enter your password and log in
This is the recommended method as it properly sets up the X session and environment variables.
Using startx with .xinitrc
If you prefer to use startx:
Create or edit
~/.xinitrc:exec nwm
Note: The
execcommand is important - it replaces the shell process with NWM. When NWM exits, the X session ends properly.Start X:
startx
Complete .xinitrc Example
A more complete ~/.xinitrc that sets up a full environment:
#!/bin/sh # Load X resources [ -f ~/.Xresources ] && xrdb -merge ~/.Xresources # Set keyboard repeat rate (delay, rate) xset r rate 200 30 # Disable screen blanking xset s off -dpms # Set wallpaper (requires feh) feh --bg-fill ~/Pictures/wallpaper.jpg & # Start compositor for transparency/shadows (requires picom) picom --config ~/.config/picom/picom.conf & # System tray applications nm-applet & # NetworkManager volumeicon & # Volume control blueman-applet & # Bluetooth manager # Auto-lock screen after 10 minutes (requires xautolock and slock) xautolock -time 10 -locker slock & # Start window manager (exec replaces the shell process with NWM) # When NWM exits, the X session ends exec nwm
Using Xinit Directly
For testing or debugging:
xinit /usr/local/bin/nwm -- :1
This starts NWM on display :1.
Testing in Xephyr
For development or testing without affecting your main session, use Xephyr (a nested X server):
# Start Xephyr on display :1 Xephyr -screen 1280x720 -ac :1 & # Run NWM in that display DISPLAY=:1 nwm
NWM includes a test script (test.sh) that automates this process.
First Steps
After starting NWM for the first time, you'll see:
- An empty desktop (no windows)
- A status bar at the bottom showing:
- Workspace indicators (1-9)
- Current layout mode ([TILE] or [SCROLL])
- Current time and date
- System information (CPU, RAM, disk, network)
Opening Your First Application
Press Super + Return to open a terminal. By default, NWM tries to launch st (Simple Terminal). If you don't have st installed, you'll need to either:
Install st:
# Arch sudo pacman -S st # Build from source git clone https://git.suckless.org/st cd st make && sudo make install
- Or change the terminal in
src/config.hpp(see Configuration section)
Using dmenu
Press Super + d to open dmenu, an application launcher. Start typing the name of an application and press Enter to launch it.
If dmenu isn't installed:
# Arch sudo pacman -S dmenu # Build from source git clone https://git.suckless.org/dmenu cd dmenu make && sudo make install
Opening Multiple Windows
Open several windows (e.g., press Super + Return three times). Notice how NWM automatically tiles them:
- The first window occupies the left half (master area)
- Additional windows stack on the right half
Switching Focus
Press Super + j and Super + k to cycle through windows. The focused window has a colored border (default: pink #FF5577).
Closing Windows
Press Super + q to close the currently focused window. Most applications will ask you to save any unsaved work.
Trying Scroll Mode
Press Super + t to toggle between tile mode and scroll mode. In scroll mode, windows are arranged side-by-side. Use Super + Left/Right arrow or Super + Mouse Wheel to scroll through them.
Understanding Layouts
NWM provides two main layout modes, each suited for different workflows.
Master-Stack Layout (Traditional Tiling)
This is the default layout mode and is similar to other tiling window managers like dwm, i3, or xmonad.
How It Works
The screen is divided into two areas:
- Master Area: The left side, typically occupied by your main window (e.g., your code editor)
- Stack Area: The right side, where additional windows are stacked vertically
Visual Representation
With one window:
┌──────────────────────┐ │ │ │ │ │ Window 1 │ │ (Fullscreen) │ │ │ │ │ └──────────────────────┘
With two windows:
┌─────────────┬────────┐ │ │ │ │ │ │ │ Window 1 │ Win 2 │ │ (Master) │ │ │ │ │ │ │ │ └─────────────┴────────┘
With three or more windows:
┌─────────────┬────────┐ │ │ Win 2 │ │ ├────────┤ │ Window 1 │ Win 3 │ │ (Master) ├────────┤ │ │ Win 4 │ │ ├────────┤ │ │ Win 5 │ └─────────────┴────────┘
Master Area Size
The master area occupies 50% of the screen width by default. You can adjust this:
Super + h: Decrease master widthSuper + l: Increase master width
The adjustment is made in increments defined by RESIZE_STEP (default: 40 pixels).
Making a Window Master
The "master" window is simply the first window in the window list. To make any window the master:
- Focus the window you want to make master
- Press
Super + Shift + hrepeatedly until it's in the first position
Use Cases
This layout is ideal for:
- Coding with a large editor and smaller auxiliary windows (terminal, browser, etc.)
- Writing with a document on the left and references on the right
- Any workflow with one primary application and several supporting ones
Horizontal Scroll Layout
This is NWM's unique feature and differentiates it from most other tiling window managers.
How It Works
Windows are arranged side-by-side in a horizontal row. Each window occupies 50% of the screen width. You scroll horizontally to see windows that don't fit on the screen.
Visual Representation
With windows 1, 2, 3 visible (viewport can show 2 windows):
┌──────────┬──────────┬──────────┐ │ │ │ │ │ Window 1 │ Window 2 │ Window 3 │ │ │ │ │ └──────────┴──────────┴──────────┘ └─ Visible ─┘ └─ Scroll right to see
After scrolling right:
┌──────────┬──────────┬──────────┐
│ │ │ │
│ Window 1 │ Window 2 │ Window 3 │
│ │ │ │
└──────────┴──────────┴──────────┘
└─ Visible ─┘
Scrolling
You can scroll through windows using:
Super + Left arrow: Scroll leftSuper + Right arrow: Scroll rightSuper + Mouse Wheel: Scroll with mouse
The scroll amount is defined by SCROLL_STEP (default: 500 pixels, but divided by 3 in practice).
Auto-scroll to Focused Window
When you focus a window that's off-screen, NWM automatically scrolls to make it visible. This happens when:
- Using
Super + jorSuper + kto change focus - Clicking on a window in the bar
- Opening a new window
Use Cases
This layout is ideal for:
- Comparing multiple files side-by-side
- Working with many terminals simultaneously
- Any task where you want to see exactly two things at once
- Presentations where you switch between different views
Toggling Between Layouts
Press Super + t to toggle between master-stack and horizontal scroll layouts. The current layout is shown in the status bar:
[TILE]: Master-stack mode[SCROLL]: Horizontal scroll mode
When switching layouts:
- Your windows remain in the same order
- The scroll offset is reset to 0
- Window focus is preserved
Gaps and Borders
Gaps
Gaps are the spaces between windows and between windows and screen edges. NWM includes gaps by default (defined by GAP_SIZE, default: 6 pixels).
To toggle gaps on/off: Super + a
With gaps disabled, windows will be directly adjacent to each other and screen edges.
Borders
Each window has a border that indicates focus:
- Unfocused border: Dark gray (
#181818by default, defined byBORDER_COLOR) - Focused border: Pink (
#FF5577by default, defined byFOCUS_COLOR)
Border width is defined by BORDER_WIDTH (default: 3 pixels).
Floating and fullscreen windows have reduced or no borders.
Configuration
NWM follows the suckless philosophy: configuration is done by editing the source code and recompiling. This gives you complete control and makes the configuration explicit and type-safe.
Configuration File Location
The main configuration file is src/config.hpp. This is a C++ header file included by the main window manager code.
Basic Configuration Structure
src/config.hpp contains:
#definemacros for simple values- Static arrays for keybindings
- Command definitions for applications
Editing and Applying Configuration
- Edit
src/config.hpp - Recompile:
make clean && make - Reinstall:
sudo make install - Restart NWM (log out and back in, or
killall nwm && nwmif running from terminal)
Appearance Configuration
Window Borders
#define BORDER_WIDTH 3 // Width in pixels #define BORDER_COLOR 0x181818 // Unfocused border (dark gray) #define FOCUS_COLOR 0xFF5577 // Focused border (pink)
Colors are in hexadecimal RGB format: 0xRRGGBB
0xFF0000= Pure red0x00FF00= Pure green0x0000FF= Pure blue0xFFFFFF= White0x000000= Black
Gaps
#define GAP_SIZE 6 // Gap in pixels between windows
Set to 0 for no gaps by default.
Bar Position
#define BAR_POSITION 1 // 0 = top, 1 = bottom
Font
#define FONT "DejaVu Sans Mono:size=10"
Font format follows Xft font specification:
"Family Name:size=SIZE""Family Name:size=SIZE:style=Bold""Family Name:size=SIZE:antialias=true"
To list available fonts:
fc-list # Or for monospace fonts only: fc-list :mono
Common choices:
"monospace:size=10"(uses system default monospace font)"Liberation Mono:size=10""Inconsolata:size=11""Fira Code:size=10""JetBrains Mono:size=10"
Workspace Labels
static const std::vector<std::string> WIDGET = {
"1","2","3","4","5","6","7","8","9"
};
You can customize these to any strings:
static const std::vector<std::string> WIDGET = {
"web", "code", "term", "chat", "mail", "media", "7", "8", "9"
};
Or use Unicode symbols:
static const std::vector<std::string> WIDGET = {
"一", "二", "三", "四", "五", "六", "七", "八", "九" // Chinese numerals
};
Layout Behavior
#define RESIZE_STEP 40 // Master resize increment in pixels #define SCROLL_STEP 500 // Horizontal scroll distance
Application Configuration
Define commands for applications you want to launch:
static const char *termcmd[] = { "st", NULL };
static const char *emacs[] = { "emacs", NULL };
static const char *browser[] = { "chromium", NULL };
Each command is a NULL-terminated array of strings. The first element is the program name, followed by any arguments:
static const char *term_float[] = { "st", "-t", "floating", NULL };
static const char *browser_priv[] = { "firefox", "--private-window", NULL };
Keybindings Configuration
Keybindings are defined in the keys[] array. Each entry consists of:
- Modifier mask (
MODKEY,MODKEY|ShiftMask, etc.) - Key symbol (
XK_Return,XK_a, etc.) - Function pointer (what to execute)
- Argument (passed to the function)
Basic Structure
static struct {
unsigned int mod; // Modifier key(s)
KeySym keysym; // Key symbol
void (*func)(void*, nwm::Base&); // Function to call
const void *arg; // Argument to pass
} keys[] = {
{ MODKEY, XK_Return, spawn, termcmd },
{ MODKEY, XK_q, close_window, NULL },
// ... more keybindings
};
Modifier Keys
#define MODKEY Mod4Mask // Super/Windows key (default)
Available modifiers:
Mod1Mask= Alt keyMod4Mask= Super/Windows keyShiftMask= Shift keyControlMask= Ctrl keyLockMask= Caps Lock
Combine modifiers with |:
MODKEY | ShiftMask // Super + Shift MODKEY | ControlMask // Super + Ctrl MODKEY | ShiftMask | Mod1Mask // Super + Shift + Alt
To change the main modifier to Alt:
#define MODKEY Mod1Mask
Key Symbols
Key symbols are X11 keysyms defined in <X11/keysym.h>. Common ones:
- Letters
XK_a through XK_z // Lowercase letters XK_A through XK_Z // Uppercase letters (use ShiftMask)
- Numbers
XK_0 through XK_9 // Number keys
- Function Keys
XK_F1 through XK_F12
- Special Keys
XK_Return // Enter XK_space // Spacebar XK_BackSpace // Backspace XK_Tab // Tab XK_Escape // Escape // Arrow keys XK_Left, XK_Right, XK_Up, XK_Down // Navigation XK_Home, XK_End, XK_Page_Up, XK_Page_Down // Other XK_Print // Print Screen XK_Insert // Insert XK_Delete // Delete
- Media Keys
XK_AudioRaiseVolume XK_AudioLowerVolume XK_AudioMute XK_AudioPlay XK_AudioStop XK_AudioPrev XK_AudioNext XK_MonBrightnessUp XK_MonBrightnessDown
Available Functions
Functions you can bind to keys:
- Application Launching
spawn: Launch an application (pass command array as argument)
- Window Management
close_window: Close focused window (argument:NULL)toggle_fullscreen: Toggle fullscreen mode (argument:NULL)toggle_float: Toggle floating mode for focused window (argument:NULL)
- Focus and Navigation
focus_next: Focus next window (argument:NULL)focus_prev: Focus previous window (argument:NULL)swap_next: Swap focused window with next (argument:NULL)swap_prev: Swap focused window with previous (argument:NULL)
- Layout
toggle_layout: Toggle between tile and scroll mode (argument:NULL)resize_master: Resize master area (argument:(void*)PIXELSor(void*)-PIXELS)scroll_left: Scroll left in scroll mode (argument:NULL)scroll_right: Scroll right in scroll mode (argument:NULL)
- Workspace
switch_workspace: Switch to workspace (argument:(void*)&wsNwhere N is workspace number)move_to_workspace: Move focused window to workspace (argument:(void*)&wsN)
- System
toggle_gap: Toggle gaps on/off (argument:NULL)toggle_bar: Toggle status bar visibility (argument:NULL)quit_wm: Quit NWM (argument:NULL)
Example Keybindings
- Launching Applications
// Define commands static const char *termcmd[] = { "st", NULL }; static const char *browser[] = { "firefox", NULL }; static const char *editor[] = { "nvim", NULL }; static const char *files[] = { "thunar", NULL }; // Bind to keys { MODKEY, XK_Return, spawn, termcmd }, { MODKEY, XK_b, spawn, browser }, { MODKEY, XK_e, spawn, editor }, { MODKEY, XK_f, spawn, files }, - Window Management
{ MODKEY, XK_q, close_window, NULL }, { MODKEY, XK_f, toggle_fullscreen, NULL }, { MODKEY|ShiftMask, XK_space, toggle_float, NULL }, - Layout Control
{ MODKEY, XK_t, toggle_layout, NULL }, { MODKEY, XK_h, resize_master, (void*)-RESIZE_STEP }, { MODKEY, XK_l, resize_master, (void*)RESIZE_STEP }, { MODKEY, XK_Left, scroll_left, NULL }, { MODKEY, XK_Right, scroll_right, NULL }, - Workspaces
// Define workspace variables static const int ws0 = 0; static const int ws1 = 1; // ... up to ws8 = 8 // Switch to workspace { MODKEY, XK_1, switch_workspace, (void*)&ws0 }, { MODKEY, XK_2, switch_workspace, (void*)&ws1 }, // ... and so on // Move window to workspace { MODKEY|ShiftMask, XK_1, move_to_workspace, (void*)&ws0 }, { MODKEY|ShiftMask, XK_2, move_to_workspace, (void*)&ws1 }, // ... and so on - Media Keys
static const char *vol_up[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%", NULL }; static const char *vol_down[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%", NULL }; static const char *vol_mute[] = { "pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle", NULL }; { 0, XK_AudioRaiseVolume, spawn, vol_up }, { 0, XK_AudioLowerVolume, spawn, vol_down }, { 0, XK_AudioMute, spawn, vol_mute },Note:
0means no modifier is required.
Mouse Bindings
Mouse bindings are hardcoded in the source (src/nwm.cpp) but can be modified:
Super + Left Click: Move floating windowSuper + Right Click: Resize floating windowSuper + Mouse Wheel: Scroll through workspaces (in scroll mode) or switch workspaces
To modify mouse behavior, edit the handle_button_press and handle_motion_notify functions in src/nwm.cpp.
Advanced Bar Configuration
The status bar's appearance is configured in src/bar.cpp. While most users won't need to edit this, you can customize:
Bar Colors
Located in src/bar.cpp:
#define BAR_HEIGHT 30 #define BAR_BG_COLOR 0x181818 // Background #define BAR_FG_COLOR 0xCCCCCC // Normal text #define BAR_ACTIVE_COLOR 0xFF5577 // Active workspace #define BAR_INACTIVE_COLOR 0x666666 // Inactive workspace #define BAR_ACCENT_COLOR 0x88AAFF // Accent (layout mode) #define BAR_WARNING_COLOR 0xFFAA00 // Warning (high CPU/RAM) #define BAR_CRITICAL_COLOR 0xFF5555 // Critical (very high usage) #define BAR_HOVER_COLOR 0x333333 // Hover background
Update Interval
The bar updates system information every 2 seconds. To change this, modify src/bar.cpp:
void nwm::bar_update_system_info(Base &base) {
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
now - base.bar.sys_info.last_update).count();
if (elapsed < 2) return; // Change this value
// ...
}
Keybindings
This section provides a complete reference of all default keybindings. Remember that Mod refers to the Super (Windows) key by default.
Application Launchers
| Keybinding | Action |
|---|---|
Mod + Return |
Launch terminal (st) |
Mod + d |
Launch dmenu (application menu) |
Mod + b |
Launch browser (chromium) |
Mod + c |
Launch editor (emacs) |
Mod + s |
Take screenshot |
Mod + Shift + s |
Take screenshot (select area) |
Mod + m |
Run custom script (master) |
Mod + z |
Launch zoomer (boomer) |
Window Management
| Keybinding | Action |
|---|---|
Mod + q |
Close focused window |
Mod + f |
Toggle fullscreen |
Mod + Shift + Space |
Toggle floating mode |
Mod + Left Click |
Drag floating window |
Mod + Right Click |
Resize floating window |
Focus and Navigation
| Keybinding | Action |
|---|---|
Mod + j |
Focus next window |
Mod + k |
Focus previous window |
Mod + Shift + h |
Swap focused window with previous |
Mod + Shift + l |
Swap focused window with next |
Layout Management
| Keybinding | Action |
|---|---|
Mod + t |
Toggle layout (tile ↔ scroll) |
Mod + h |
Decrease master window size |
Mod + l |
Increase master window size |
Mod + a |
Toggle gaps on/off |
Mod + r |
Toggle status bar visibility |
Horizontal Scroll (Scroll Mode Only)
| Keybinding | Action |
|---|---|
Mod + Left |
Scroll left |
Mod + Right |
Scroll right |
Mod + Mouse Wheel |
Scroll horizontally |
Workspace Management
| Keybinding | Action |
|---|---|
Mod + 1-9 |
Switch to workspace 1-9 |
Mod + Shift + 1-9 |
Move focused window to workspace 1-9 |
Mouse Wheel (on bar) |
Scroll through workspaces |
Left Click (on bar) |
Switch to clicked workspace |
System
| Keybinding | Action |
|---|---|
Mod + Shift + q |
Quit NWM |
Window Management
This section explains how windows are managed in NWM, including tiling, floating, and fullscreen modes.
Window States
A window in NWM can be in one of several states:
Tiled
- Default state for most windows
- Managed by the active layout (master-stack or horizontal scroll)
- Cannot be moved or resized directly with the mouse
- Position and size determined by the layout algorithm
Floating
- Window can be freely moved and resized
- Always drawn on top of tiled windows
- Useful for dialogs, utility windows, or when you need precise positioning
- Toggle with
Mod + Shift + Space
Fullscreen
- Window covers the entire screen, including the bar
- All other windows are hidden
- Border is removed
- Toggle with
Mod + f
Auto-Float Detection
NWM automatically makes certain windows float based on their properties:
Window Types That Auto-Float
- Dialog windows (
_NET_WM_WINDOW_TYPE_DIALOG) - Splash screens (
_NET_WM_WINDOW_TYPE_SPLASH) - Utility windows (
_NET_WM_WINDOW_TYPE_UTILITY) - Windows with
_NET_WM_STATE_MODALstate - Windows with
_NET_WM_STATE_ABOVEstate - Transient windows (windows with
WM_TRANSIENT_FORhint) - Windows with fixed size (minsize == maxsize and < 800x600)
Examples of Auto-Floating Windows
- Firefox's "Save As" dialog
- GIMP's tool windows
- Application preferences windows
- File picker dialogs
- Error/warning dialogs
If a window auto-floats and you want it tiled, press Mod + Shift + Space to toggle it.
Window Ignoring
Some windows are completely ignored by NWM and aren't managed:
Ignored Window Types
- Desktop windows (
_NET_WM_WINDOW_TYPE_DESKTOP) - Dock windows (
_NET_WM_WINDOW_TYPE_DOCK) - Notification windows (
_NET_WM_WINDOW_TYPE_NOTIFICATION) - Tooltip windows (
_NET_WM_WINDOW_TYPE_TOOLTIP) - Menu windows (dropdown, popup, combo)
- Windows with
override_redirectflag
Examples
- Desktop environment panels (if any)
- Notification daemons (Dunst, notify-osd)
- Tooltip popups
- Dropdown menus
These windows appear and disappear as needed and are always on top.
Moving Windows
Within the Current Workspace
In tile mode:
Mod + Shift + h: Swap focused window with previousMod + Shift + l: Swap focused window with next
In scroll mode:
- Same keybindings work
- When you swap, the scroll position adjusts to keep the focused window visible
Floating windows:
Mod + Left Clickand drag
Between Workspaces
Mod + Shift + [1-9]: Move focused window to workspace [1-9]- The window disappears from current workspace and appears in target workspace
- Focus remains on current workspace (window moves but you don't follow)
Resizing Windows
Tiled Windows in Master-Stack Mode
Mod + h: Decrease master area width (increases stack area)Mod + l: Increase master area width (decreases stack area)
This affects the master/stack split ratio. All tiled windows are then resized to fit the new ratio.
Tiled Windows in Scroll Mode
Window sizes in scroll mode are fixed at 50% screen width and full height (minus bar and gaps). Manual resizing isn't available in scroll mode.
Floating Windows
Mod + Right Clickand drag: Resize from bottom-right corner- Minimum size enforced: 100x100 pixels
Fullscreen Windows
Fullscreen windows cannot be resized while in fullscreen mode. Exit fullscreen first (Mod + f).
Closing Windows
Press Mod + q to close the focused window.
How It Works
NWM sends a WM_DELETE_WINDOW message to the window, which is the polite way to ask an X11 application to close. This allows the application to:
- Save unsaved work
- Show a "Are you sure?" dialog
- Clean up resources
- Close gracefully
If a Window Won't Close
Some misbehaving applications may ignore the close request. In that case:
# Find the window's process xprop _NET_WM_PID | grep -o '[0-9]*' # Click on the window when cursor changes # Kill the process kill <PID> # Or force kill kill -9 <PID>
Or use xkill:
xkill # Click on the window to kill it
Focus Model
NWM uses "focus follows mouse" by default. This means:
Focus Behavior
- Moving the mouse cursor over a window automatically focuses it
- You don't need to click to focus
- The focused window receives keyboard input
- Only one window can be focused at a time
Visual Indication
- Focused window has a colored border (default: pink
#FF5577) - Unfocused windows have a dark gray border (default:
#181818) - Focused workspace in bar is highlighted
Manual Focus Control
Mod + j: Focus next window (cycles through all windows)Mod + k: Focus previous window (cycles in reverse)
When you manually change focus, the mouse cursor doesn't move. To avoid accidentally refocusing when moving the mouse, some users prefer click-to-focus. This would require modifying the source code to remove EnterWindowMask from window event masks.
Window Ordering and Stacking
In Tile Mode
- Tiled windows don't overlap, so stacking order doesn't matter much
- Floating windows are always drawn above tiled windows
- Fullscreen window is drawn above everything (except ignored windows like notifications)
In Scroll Mode
- Same as tile mode
- Windows are arranged in a horizontal row
- Order in the row matches the order in the window list
Master Position
- The "master" window is the first window in the workspace's window list
- It's not a special property, just the position in the list
- Any window can become master by being swapped to position 0
Changing Order
Mod + Shift + h: Move current window earlier in list (toward position 0)Mod + Shift + l: Move current window later in list
Workspace Management
Workspaces (also called "tags" or "virtual desktops" in other window managers) allow you to organize windows into separate groups.
Understanding Workspaces
NWM provides 9 workspaces by default (can be changed by recompiling with a different NUM_WORKSPACES value).
What Are Workspaces?
Think of workspaces as separate desktops, each with its own set of windows. Only one workspace is visible at a time. Switching workspaces is instant.
Properties of Each Workspace
- Independent window list
- Independent layout mode (each workspace can be in tile or scroll mode)
- Independent scroll offset (for scroll mode)
- Independent master area size (for tile mode)
- Independent focused window
Use Cases
Common ways to organize workspaces:
- Workspace 1: Web browser
- Workspace 2: Code editor and terminals
- Workspace 3: Email client
- Workspace 4: Chat applications (Slack, Discord, etc.)
- Workspace 5: Music player
- Workspace 6-9: Additional tasks
Or by project:
- Workspace 1: Project A (editor, terminals, browser)
- Workspace 2: Project B
- Workspace 3: Project C
- etc.
Switching Workspaces
Keyboard
Mod + 1: Switch to workspace 1Mod + 2: Switch to workspace 2- …
Mod + 9: Switch to workspace 9
Switching workspaces:
- Unmaps (hides) all windows in current workspace
- Changes current workspace to target
- Maps (shows) all windows in target workspace
- Restores focus to the last focused window in target workspace
Mouse (via Status Bar)
- Click on a workspace indicator (the numbers in the bar)
- Scroll the mouse wheel over the bar to cycle through workspaces
Visual Feedback
The status bar shows all workspaces:
- Active workspace: Highlighted background (default: darker gray)
- Workspaces with windows: Normal background
- Empty workspaces: Dimmed
Moving Windows Between Workspaces
Mod + Shift + [1-9]: Move focused window to workspace [1-9]
What Happens
- Window is removed from current workspace's window list
- Window is added to target workspace's window list
- Window is unmapped (hidden)
- Focus moves to next window in current workspace (if any)
- Window will be visible when you switch to target workspace
Important Notes
- Moving a window doesn't switch workspaces
- You stay in the current workspace after moving a window
- If you move the only window, the workspace becomes empty
- You can move floating and fullscreen windows (they exit fullscreen first)
Empty Workspaces
An empty workspace has no windows in it. This is normal and fine.
Behavior
- Shows the desktop (wallpaper if set)
- Shows only the status bar
- Pressing
Mod + j/k(focus next/prev) does nothing - Opening a new window automatically places it in the current workspace
Workspace Persistence
What's Preserved
- Window positions in the window list
- Layout mode (tile vs scroll)
- Master area size
- Scroll offset
What's Not Preserved
- Workspaces are not saved between sessions
- When you quit NWM, all workspace information is lost
- On next startup, all existing windows go to workspace 0
Session Management
For persistence across reboots, you'd need session management (not currently implemented in NWM). Most users simply reopen their applications and reorganize.
Alternative: Use a session manager like tmux for terminals, and browser session restore for web browsers.
Changing Number of Workspaces
To have more or fewer workspaces:
Edit
src/nwm.hpp:#define NUM_WORKSPACES 12 // Change from 9 to desired number
Edit
src/config.hppto add workspace labels:static const std::vector<std::string> WIDGET = { "1","2","3","4","5","6","7","8","9","10","11","12" };Add keybindings for the new workspaces in
src/config.hpp:static const int ws9 = 9; static const int ws10 = 10; static const int ws11 = 11; // In keys[] array: { MODKEY, XK_0, switch_workspace, (void*)&ws9 }, // Note: You might need to use different keys or key combinations // since keyboard only has 1-9 number keys- Recompile:
make clean && make && sudo make install
The Status Bar
The status bar provides information about your workspaces and system at a glance.
Bar Layout
The bar is divided into several sections:
┌─────────────────────────────────────────────────────────────────┐ │ [1][2][3][4].. [TILE] │ 12:30 Mon Jan 15 │ CPU 15% RAM 45% .. │ └─────────────────────────────────────────────────────────────────┘ └─ Workspaces ─┘└─ Mode─┘└──── Time ────────┘└── System Info ───┘
Workspace Indicators
The left side shows all workspaces:
Visual States
- Active workspace: Dark background, pink text
- Workspace with windows: Medium background, white text
- Empty workspace: No background, gray text
- Hover: Light background when mouse is over it
Interaction
- Click: Switch to that workspace
- Scroll wheel: Cycle through workspaces
- Hover: Highlights to show it's clickable
Layout Indicator
Shows current layout mode:
[TILE]: Master-stack tiling mode[SCROLL]: Horizontal scroll mode
This updates immediately when you toggle with Mod + t.
Time and Date
Displays current time and date in the center:
- Format:
HH:MM Day Mon DD - Example:
14:30 Mon Jan 15 - Updates every second
System Information
The right side shows system stats:
Displayed Information
- CPU: CPU usage percentage
- RAM: Memory usage percentage
- DISK: Disk usage percentage for root partition (
~/) - DOWN: Download speed (KB/s or MB/s)
- UP: Upload speed (KB/s or MB/s)
- BAT: Battery percentage (if present)
- CHG: Shows when charging
Update Interval
- System info updates every 2 seconds
- Network speeds are calculated since last update
Color Coding
- Normal: Light gray text
- Warning: Orange text (CPU or RAM > 75%)
- Critical: Red text (CPU or RAM > 90%)
System Tray
The system tray appears on the right side of the bar, between the system info and the edge.
What Appears Here
- NetworkManager icon
- Volume control icon
- Bluetooth icon
- Notification icons
- Any application that uses the system tray protocol
Icons are 20×20 pixels by default with 4px padding between them.
Toggling Bar Visibility
Press Mod + r to hide/show the status bar.
When Hidden
- Windows expand to use the full vertical space
- All bar functionality is lost (can't click workspaces, see time, etc.)
- System tray icons are also hidden
When to Hide
- Presentations or screen recordings
- Maximizing screen space for reading/viewing
- Playing fullscreen games (though fullscreen mode already covers the bar)
Bar Position
The bar can be at the top or bottom of the screen. This is configured in src/config.hpp:
#define BAR_POSITION 1 // 0 = top, 1 = bottom
Top Bar (BAR_POSITION 0)
- Bar at top, windows below
- Traditional placement
- Easier to see when focused on top of screen
Bottom Bar (BAR_POSITION 1)
- Bar at bottom, windows above
- Default in NWM
- Keeps bar near taskbar location in other DEs
- Easier to access with mouse (less distance to move)
After changing, recompile and restart NWM.
Customizing Bar Appearance
See the "Advanced Bar Configuration" section in Configuration for details on changing colors, fonts, and update intervals.
System Tray
The system tray (also called notification area) allows applications to display small status icons in the bar.
What is the System Tray?
The system tray is a common desktop feature where applications can place small icons to indicate status, provide quick access, or show notifications.
Common System Tray Applications
- Network managers: NetworkManager (nm-applet), ConnMan, wicd
- Volume controls: volumeicon, pasystray
- Bluetooth: blueman-applet, blueberry
- Cloud storage: Dropbox, Nextcloud, Google Drive
- Messaging: Slack, Discord, Telegram (minimized)
- Media players: Spotify, VLC (with tray plugin)
- System monitors: CPU/memory monitors, battery monitors
Using the System Tray
Starting Tray Applications
Start applications normally, and they'll add their icon to the tray:
nm-applet & volumeicon & blueman-applet &
Add these to your ~/.xinitrc to start automatically:
#!/bin/sh # ... other startup commands nm-applet & volumeicon & blueman-applet & exec nwm
Interacting with Tray Icons
- Left click: Usually shows the main window or menu
- Right click: Usually shows a context menu
- Middle click: Application-specific action
Each application defines its own behavior.
Tray Icon Appearance
- Icons are 20×20 pixels
- Background matches bar background
- Icons are raised above the bar
- 4px padding between icons
Technical Details
NWM implements the _NET_SYSTEM_TRAY protocol, specifically:
_NET_SYSTEM_TRAY_Snselection (where n is screen number)- XEMBED protocol for embedding windows
_NET_SYSTEM_TRAY_OPCODEfor dock requests
Supported Features
- Multiple tray icons
- Icon removal and addition
- Dynamic reordering
- 32-bit ARGB visuals (transparency support)
- Horizontal orientation
Limitations
- Single system tray per X screen
- No icon size customization per icon (all icons are same size)
- No icon tooltips (applications may implement their own)
Troubleshooting Tray Issues
Icons Not Appearing
Check if another tray is running:
xprop -root _NET_SYSTEM_TRAY_S0
If this shows a window ID, another tray owns the selection.
- Start the application after NWM: System tray applications need the tray to exist before they start. If you start the app before NWM, it won't see the tray.
Restart the application:
killall nm-applet && nm-applet &
Icons Are Too Large/Small
Icon size is hardcoded in src/systray.cpp:
#define TRAY_ICON_SIZE 20
Change this value and recompile to adjust icon size.
Tray Icons Overlap System Info
This is normal if you have many tray icons. The system info shifts left to make room. If it's a problem:
- Close some tray applications
- Hide less important system info (requires code modification)
- Use a longer bar by increasing screen width (not really a solution)
Disabling the System Tray
If you don't use the system tray and want to disable it:
Comment out the initialization in src/nwm.cpp:
void nwm::init(Base &base) {
// ... other initialization
// systray_init(base); // Comment this out
// ... rest of init
}
And in the cleanup:
void nwm::cleanup(Base &base) {
// ... other cleanup
// systray_cleanup(base); // Comment this out
// ... rest of cleanup
}
Recompile and reinstall.
Advanced Configuration
This section covers advanced topics for users who want to deeply customize NWM.
Adding Custom Functions
You can add entirely new functionality by writing C++ functions and binding them to keys.
Example: Toggle Window Opacity
Add function declaration in
src/nwm.hpp:void toggle_opacity(void *arg, Base &base);
Implement in
src/nwm.cpp:void nwm::toggle_opacity(void *arg, Base &base) { (void)arg; if (!base.focused_window) return; // Toggle between opaque and semi-transparent static bool is_transparent = false; is_transparent = !is_transparent; unsigned long opacity = is_transparent ? 0xDDFFFFFF : 0xFFFFFFFF; Atom opacity_atom = XInternAtom(base.display, "_NET_WM_WINDOW_OPACITY", False); XChangeProperty(base.display, base.focused_window->window, opacity_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&opacity, 1); }Add keybinding in
src/config.hpp:{ MODKEY, XK_o, toggle_opacity, NULL },- Recompile and install
Example: Cycle Through Layouts
Add a function to cycle through multiple layouts (not just two):
void nwm::cycle_layouts(void *arg, Base &base) {
(void)arg;
static int current_layout = 0;
current_layout = (current_layout + 1) % 3;
switch(current_layout) {
case 0:
base.horizontal_mode = false;
tile_windows(base);
break;
case 1:
base.horizontal_mode = true;
tile_horizontal(base);
break;
case 2:
// Implement a grid layout or monocle layout here
break;
}
bar_draw(base);
}
Modifying Layout Algorithms
The tiling algorithms are in src/tiling.cpp.
Creating a Monocle Layout
Monocle layout shows one window at a time, fullscreen (but with bar visible):
void nwm::tile_monocle(Base &base) {
auto ¤t_ws = get_current_workspace(base);
if (current_ws.windows.empty()) return;
int screen_width = WIDTH(base.display, base.screen);
int screen_height = HEIGHT(base.display, base.screen);
int bar_height = base.bar_visible ? base.bar.height : 0;
int usable_height = screen_height - bar_height;
int y_start = (base.bar_position == 0) ? bar_height : 0;
// Hide all windows except focused
for (auto &w : current_ws.windows) {
if (w.window == base.focused_window->window) {
w.x = 0;
w.y = y_start;
w.width = screen_width;
w.height = usable_height;
XMoveResizeWindow(base.display, w.window, w.x, w.y, w.width, w.height);
XMapWindow(base.display, w.window);
} else {
XUnmapWindow(base.display, w.window);
}
}
XFlush(base.display);
}
Then add this to a layout cycle or bind it to a key.
Creating a Grid Layout
Grid layout arranges windows in a grid:
void nwm::tile_grid(Base &base) {
auto ¤t_ws = get_current_workspace(base);
std::vector<ManagedWindow*> tiled_windows;
for (auto &w : current_ws.windows) {
if (!w.is_floating && !w.is_fullscreen) {
tiled_windows.push_back(&w);
}
}
if (tiled_windows.empty()) return;
int screen_width = WIDTH(base.display, base.screen);
int screen_height = HEIGHT(base.display, base.screen);
int bar_height = base.bar_visible ? base.bar.height : 0;
int usable_height = screen_height - bar_height;
int y_start = (base.bar_position == 0) ? bar_height : 0;
// Calculate grid dimensions
int cols = std::ceil(std::sqrt(tiled_windows.size()));
int rows = std::ceil((float)tiled_windows.size() / cols);
int win_width = screen_width / cols - 2 * base.gaps;
int win_height = usable_height / rows - 2 * base.gaps;
for (size_t i = 0; i < tiled_windows.size(); ++i) {
int col = i % cols;
int row = i / cols;
tiled_windows[i]->x = col * (win_width + 2 * base.gaps) + base.gaps;
tiled_windows[i]->y = row * (win_height + 2 * base.gaps) + base.gaps + y_start;
tiled_windows[i]->width = win_width;
tiled_windows[i]->height = win_height;
XMoveResizeWindow(base.display, tiled_windows[i]->window,
tiled_windows[i]->x, tiled_windows[i]->y,
tiled_windows[i]->width, tiled_windows[i]->height);
}
XFlush(base.display);
}
Multi-Monitor Support
NWM currently doesn't support multiple monitors natively. However, you can use Xinerama or RandR to treat multiple monitors as one large screen.
Using xrandr
# List monitors xrandr # Arrange monitors xrandr --output HDMI-1 --auto --left-of eDP-1
Add to ~/.xinitrc to make permanent (before exec nwm).
NWM will treat the combined area as one screen. You can use workspaces to separate monitors logically (e.g., workspaces 1-5 on left monitor, 6-9 on right).
True Multi-Monitor Support
Implementing true multi-monitor support would require:
- Detecting monitors with Xinerama or RandR
- Creating separate workspaces per monitor
- Modifying tiling algorithms to work per-monitor
- Handling focus across monitors
This is a significant undertaking and not currently planned, but contributions are welcome.
Startup Hooks
Currently, NWM doesn't have built-in startup hooks. Use ~/.xinitrc or systemd user services for startup tasks:
Using .xinitrc
#!/bin/sh # Custom startup script ~/.config/nwm/startup.sh & exec nwm
Using Systemd User Services
Create ~/.config/systemd/user/nwm-startup.service:
[Unit] Description=NWM Startup Tasks After=graphical-session.target [Service] Type=oneshot ExecStart=/home/yourusername/.config/nwm/startup.sh [Install] WantedBy=graphical-session.target
Enable:
systemctl --user enable nwm-startup.service
Custom Bar Widgets
The bar is rendered in src/bar.cpp. You can add custom widgets by modifying the bar_draw function.
Example: Adding Weather
Create a function to fetch weather:
std::string get_weather() { // Use curl or libcurl to fetch from weather API // Parse JSON response // Return formatted string like "☀️ 72°F" return "☀️ 72°F"; }Add to bar in
bar_draw:std::string weather = get_weather(); XftDrawStringUtf8(base.bar.xft_draw, &base.bar.xft_fg, base.xft_font, weather_x, y_offset, (XftChar8*)weather.c_str(), weather.length());- Call periodically (every 10 minutes) in the main event loop
IPC (Inter-Process Communication)
NWM doesn't currently implement IPC, but you could add it:
Using Unix Domain Sockets
Create a socket in init that listens for commands:
// Pseudo-code
void nwm::init_ipc(Base &base) {
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
// Bind to /tmp/nwm-socket
// Listen for connections
// Handle commands in event loop
}
Then you could create a client program:
#!/bin/bash # nwm-msg - Send message to NWM echo "$1" | nc -U /tmp/nwm-socket
Usage:
nwm-msg "workspace 2" nwm-msg "toggle-layout"
Using X11 Properties
Simpler approach: set X properties on root window:
void nwm::check_commands(Base &base) {
Atom command_atom = XInternAtom(base.display, "NWM_COMMAND", False);
// Read property
// Execute command
// Delete property
}
Call periodically or on PropertyNotify events.
Client:
xprop -root -f NWM_COMMAND 8s -set NWM_COMMAND "workspace 2"
Session Management
To save and restore window positions:
- On quit, save window information to
~/.config/nwm/session - On start, read session file and match windows to saved positions
This requires:
- Identifying windows (by WMCLASS, WMNAME, etc.)
- Saving workspace, position, size, state
- Re-managing windows after they're mapped
Complex but doable. i3 has session management you can reference.
Troubleshooting
NWM Won't Start
Symptom: Black screen or immediate return to login
Possible causes and solutions:
Missing dependencies
# Verify X11 libraries ldd /usr/local/bin/nwm # Should show no "not found"
- Font not found
- Check
~/.xsession-errorsor/var/log/Xorg.0.log Change FONT in
src/config.hppto a font you have:fc-list | grep -i mono
- Check
- Another WM is running
- Only one window manager can control the X server at a time
- Kill other WM:
killall openboxorkillall i3
X server not running
echo $DISPLAY # Should show :0 or :1
Debugging
Run NWM from a terminal to see error messages:
# In an existing X session nwm # Or in Xephyr for testing Xephyr -screen 1280x720 :1 & DISPLAY=:1 nwm
Check logs:
cat ~/.xsession-errors tail -f /var/log/Xorg.0.log
Status Bar Not Showing
Symptom: Bar is missing or blank
- Bar hidden
- Press
Mod + rto toggle bar visibility
- Press
- Font rendering issue
Install required font:
sudo pacman -S ttf-dejavu # or sudo apt install fonts-dejavu
- Or change FONT in config
Xft library missing
ldd /usr/local/bin/nwm | grep Xft
- Bar colors same as background
- Check BARFGCOLOR and BARBGCOLOR are different
Keybindings Not Working
Symptom: Pressing key combinations does nothing
- Wrong modifier key
- Verify Super key works:
xevand press Super key - Check output for
Mod4 - If not, change to Alt:
#define MODKEY Mod1Mask
- Verify Super key works:
- Key conflict
- Another program may be grabbing the key
- Check with:
xev(press key and see if events appear)
- NumLock/CapsLock interference
- NWM tries to handle this, but some keyboards are tricky
- Try disabling NumLock
- Keybinding not compiled in
- Check
src/config.hppfor the keybinding - Recompile:
make clean && make && sudo make install
- Check
Testing Keys
Use xev:
xev # Press keys and observe output # Look for KeyPress events with keysym names
Windows Not Tiling
Symptom: Windows float instead of tiling
- Window is meant to float
- Dialogs, splash screens, etc. auto-float
- Toggle:
Mod + Shift + Space
- Only one window
- A single window fills the screen
- Open more windows to see tiling
- In scroll mode with one window visible
- Scroll to see other windows:
Mod + Left/Right
- Scroll to see other windows:
- Window manager not actually managing the window
- Window might be override-redirect (like dmenu)
- Check with:
xprop(click on window, look foroverride redirect: True)
High CPU Usage
Symptom: NWM using significant CPU
Likely causes:
- Frequent bar updates
- The bar redraws and updates system info every 2 seconds
- Normal CPU usage: 0-2%
- If higher, check for bugs in system info gathering
- Many tray icons
- Each tray icon is a separate X window
- More icons = more overhead
- X11 performance
- Some X drivers are slow
- Try different compositor settings or disable compositor
- Infinite loop or bug
- If CPU is constantly high (>50%), there's a bug
- Check with:
toporhtop - Report bug with details
Reducing CPU Usage
- Increase bar update interval (edit
src/bar.cpp) - Close unused tray applications
- Disable picom/compositor
System Tray Icons Not Appearing
Symptom: Tray icons missing
- Started before NWM
Solution: Restart the application
killall nm-applet && nm-applet &
- Another tray is running
- Only one system tray can run at a time
Check:
xprop -root | grep SYSTEM_TRAY
- Kill other tray or quit other WM
- Application doesn't support system tray
- Not all applications have tray icons
- Check application documentation
Mouse Not Working for Window Operations
Symptom: Can't drag or resize windows with mouse
- Wrong modifier key
- Must hold
Mod(Super) key while dragging/resizing - Try Alt if Super doesn't work
- Must hold
- Window is tiled
- Tiled windows can't be moved/resized with mouse
- Toggle floating:
Mod + Shift + Space
- Mouse bindings not grabbed
- Check
src/nwm.cppforXGrabButtoncalls - Should grab Button1 and Button3 with MODKEY
- Check
Window Focus Issues
Symptom: Can't focus window or wrong window is focused
- Mouse outside window
- Focus follows mouse
- Move mouse over the window you want focused
- Window is unmapped
- Check if window is on current workspace
- Switch workspaces:
Mod + 1-9
- Window is behind floating window
- Floating windows are always on top
- Close or move floating window
Application-Specific Issues
Java Applications (IntelliJ, Android Studio, etc.)
Java applications may not recognize NWM. Add to ~/.xinitrc before exec nwm:
export _JAVA_AWT_WM_NONREPARENTING=1
Or before starting the application:
_JAVA_AWT_WM_NONREPARENTING=1 idea.sh
Electron Applications
Some Electron apps have issues with tiling WMs. Try:
# For VS Code code --disable-gpu
Games
Fullscreen games should work with Mod + f for fullscreen mode. If not:
# Force window mode game-binary -windowed
Wine Applications
Wine apps may need:
# In wine config winetricks settings # Enable "Emulate a virtual desktop"
Crashes and Segfaults
Symptom: NWM crashes or displays "Segmentation fault"
Recompile with debug info
make clean CXXFLAGS = "-std=c++14 -O3 -Wall -Wextra -Wpedantic -Werror -Wstrict-aliasing -g" make
Run with gdb
gdb /usr/local/bin/nwm (gdb) run # Wait for crash (gdb) backtrace
- Report bug
- Copy backtrace
- Note what you were doing when it crashed
- Open issue on GitHub with details
Getting More Help
If these troubleshooting steps don't help:
- Check existing issues
- https://github.com/xsoder/nwm/issues
- Your problem may already be reported and solved
- Ask for help
- Open a new issue with:
- OS and version
- NWM version (
git rev-parse HEAD) - Contents of
~/.xsession-errors - Steps to reproduce
- Expected vs actual behavior
- Open a new issue with:
- Enable verbose logging
- Add debug
printfstatements - Recompile and observe output
- Add debug
Contributing
NWM is a hobby project and welcomes contributions!
Ways to Contribute
Report Bugs
Found a bug? Please open an issue on GitHub with:
- Operating system and version
- NWM version (git commit hash)
- Steps to reproduce the bug
- Expected behavior vs actual behavior
- Relevant log output from
~/.xsession-errors - Screenshots if applicable
Suggest Features
Have an idea? Open an issue with the "enhancement" label.
Please note: NWM aims to stay relatively simple and focused. Not all feature requests will be accepted. Features that significantly increase complexity or add many dependencies are less likely to be included.
Preferred features:
- Improvements to existing functionality
- Bug fixes
- Performance optimizations
- Better documentation
- Cleaner code organization
Less likely to be accepted:
- Multi-monitor support (complex, better handled by other WMs)
- External configuration files (goes against suckless philosophy)
- Built-in application launchers (use dmenu/rofi)
- Extensive theming system (just edit the source)
Submit Code
Pull requests are welcome! Please:
Fork the repository
# On GitHub, click "Fork" git clone https://github.com/YOUR_USERNAME/nwm.git cd nwm
Create a branch
git checkout -b feature/my-new-feature # or git checkout -b fix/bug-description
- Make your changes
- Follow existing code style
- Add comments for complex logic
- Test thoroughly
- Test your changes
- Use the included
test.shscript - Test in Xephyr before testing on main session
- Ensure existing functionality still works
- Use the included
Commit with clear messages
git commit -m "Add feature: description of feature" # or git commit -m "Fix bug: description of bug and solution"
Push and create pull request
git push origin feature/my-new-feature # Then open PR on GitHub
Improve Documentation
Documentation is always in need of improvement:
- Fix typos and grammatical errors
- Clarify confusing sections
- Add examples
- Translate to other languages
- Create tutorials or guides
- Make video walkthroughs
Documentation contributions are just as valuable as code!
Spread the Word
Help others discover NWM:
- Star the repository on GitHub
- Share on social media or forums
- Write blog posts about your experience
- Create rice/showcase posts with NWM
Code Style Guidelines
To keep the codebase consistent:
C++ Style
- Indentation: 4 spaces (no tabs)
Braces: Opening brace on same line
if (condition) { // code }- Naming:
- Functions:
snake_case - Variables:
snake_case - Classes/Structs:
PascalCase - Constants:
UPPER_CASE
- Functions:
- Comments: Use
//for single-line,/* */for multi-line - Includes: System headers first, then local headers
File Organization
- Header files:
.hpp - Implementation:
.cpp - Keep headers minimal (declarations only)
- Implementation in .cpp files
Commit Messages
Follow these conventions:
- First line: Brief summary (50 chars or less)
- Blank line
- Detailed explanation if needed
Add horizontal scroll layout Implements a new layout mode where windows are arranged side-by-side and can be scrolled through horizontally. This provides an alternative to the traditional master-stack layout.
Development Setup
Requirements
- Git
- C++ compiler (GCC 5+ or Clang 3.4+)
- Make
- X11 development libraries
- Text editor (vim, emacs, VS Code, etc.)
- Xephyr (for testing)
Building for Development
# Clone your fork git clone https://github.com/YOUR_USERNAME/nwm.git cd nwm # Build make # Test in Xephyr ./test.sh # Or manually: Xephyr -screen 1280x720 :1 & DISPLAY=:1 ./nwm
Debugging
Compile with debug symbols:
make clean CXXFLAGS = "-std=c++14 -O3 -Wall -Wextra -Wpedantic -Werror -Wstrict-aliasing -g" make
Run in gdb:
Xephyr -screen 1280x720 :1 & DISPLAY=:1 gdb ./nwm (gdb) run
Or with valgrind for memory leaks:
DISPLAY=:1 valgrind --leak-check=full ./nwm
Code Structure
Understanding the codebase:
src/nwm.cpp
- Main window manager logic
- Event loop
- Window management (manage, unmanage, focus)
- Event handlers (key press, button press, map request, etc.)
- Initialization and cleanup
src/nwm.hpp
- Main header file
- Structure definitions (
Base,ManagedWindow,Workspace) - Function declarations
- Macros
src/tiling.cpp
- Layout algorithms
tile_windows(): Master-stack layouttile_horizontal(): Horizontal scroll layout- Window arrangement functions
src/bar.cpp
- Status bar rendering
- System information gathering (CPU, RAM, disk, network, battery)
- Workspace indicators
- Time display
- Mouse interaction with bar
src/bar.hpp
- Bar structure definitions
- Bar function declarations
src/systray.cpp
- System tray implementation
- XEMBED protocol
- Tray icon management
*####
src/systray.hpp- System tray structures
- System tray function declarations
src/config.hpp
- User configuration
- Keybindings array
- Application commands
- Visual settings (colors, fonts, gaps)
Makefile
- Build configuration
- Compiler flags
- Installation targets
flake.nix
- Nix package definition
- Development environment
Testing
Before submitting a pull request:
- Test basic functionality
- Opening/closing windows
- Switching workspaces
- Changing layouts
- Resizing and moving windows
- Fullscreen mode
- Floating windows
- Test with multiple applications
- Terminals
- Browsers
- Text editors
- Floating dialogs
- Test error cases
- What happens with no windows?
- What if you try to close the last window?
- What if a keybinding has a NULL argument when it expects one?
Check for memory leaks
valgrind --leak-check=full ./nwm # Run for a while, open/close windows, switch workspaces # Exit and check for leaks
- Test on real hardware
- Xephyr is great for development, but test on actual X session before finalizing
License
NWM is licensed under the MIT License. By contributing, you agree that your contributions will be licensed under the same license.
See the LICENSE file for the full license text.
Code of Conduct
Be respectful and constructive:
- Respect different opinions and experiences
- Accept constructive criticism gracefully
- Focus on what's best for the project and community
- Show empathy towards other contributors
Unacceptable behavior:
- Harassment or discriminatory language
- Personal attacks
- Trolling or insulting comments
- Publishing others' private information
Violations may result in removal of contributions or ban from the project.
Questions?
If you're unsure about something:
- Open an issue for discussion
- Ask in a pull request
- Check existing issues and PRs for similar questions
Don't be afraid to ask! Everyone was a beginner once.
Appendix
Glossary
- Bar/Status Bar: The horizontal strip (usually at top or bottom) showing information
- Compositor: Software that provides visual effects (transparency, shadows, animations)
- Desktop Environment (DE): A complete graphical interface (GNOME, KDE, XFCE)
- Display Manager: Login screen software (LightDM, GDM, SDDM)
- dmenu: Dynamic menu application launcher
- dwm: Dynamic Window Manager, a minimalist tiling WM from suckless
- Float/Floating: Window that can be freely moved and resized
- Focus: The window that receives keyboard input
- Master: The primary window in master-stack layout
- picom: Compositor for X11 (formerly compton)
- Stack: Secondary area in master-stack layout with vertically stacked windows
- Suckless: Philosophy and organization behind dwm, st, dmenu
- System Tray: Area where applications place notification icons
- Tiled/Tiling: Windows arranged automatically without overlaps
- Window Manager (WM): Software that controls window placement and appearance
- Workspace/Virtual Desktop/Tag: Separate window groups
- X11/X Window System: Display server protocol for Unix-like systems
- Xephyr: Nested X server for testing
- Xft: X FreeType library for font rendering
Comparison with Other Window Managers
vs dwm
NWM is inspired by dwm but differs in:
- Language: C++14 vs C
- Patches: Direct source editing vs patch system
- Layouts: Includes horizontal scroll layout
- Bar: Built-in status bar with system info
- System Tray: Built-in support
Similar to dwm:
- Configuration through source
- Minimal by default
- Master-stack layout
- Keyboard-focused
- Fast and lightweight
vs i3
i3 is more feature-complete but:
- Configuration: i3 uses config file, NWM uses source
- Layouts: i3 has more layout modes, NWM has scroll mode
- IPC: i3 has comprehensive IPC, NWM doesn't (yet)
- Multi-monitor: i3 supports multiple monitors natively
NWM is simpler and more hackable, i3 is more powerful and configurable without recompiling.
vs bspwm
bspwm is also minimal but:
- Configuration: bspwm uses external config (bspc), NWM uses source
- Layout: bspwm uses binary tree, NWM uses master-stack/scroll
- Philosophy: bspwm is more Unix-philosophy (separate concerns)
vs xmonad
xmonad is configured in Haskell:
- Language: Haskell vs C++
- Flexibility: xmonad is extremely flexible, NWM is simpler
- Learning curve: xmonad requires Haskell knowledge
vs awesome
awesome is configured in Lua:
- Language: Lua for config vs C++ for NWM
- Features: awesome has more widgets and features
- Complexity: awesome is more complex
NWM is simpler and more focused on core tiling functionality.
Useful Resources
Window Manager Resources
Similar Projects
X11 Programming
Community
- NWM GitHub Repository
- NWM Issues
- NWM Discussions
- r/unixporn - For showcasing your setup
Changelog
Version 0.1.0 (Initial Release)
- Master-stack tiling layout
- Horizontal scroll layout
- 9 workspaces
- Built-in status bar with system information
- System tray support
- Fullscreen and floating window support
- Mouse support for moving/resizing
- Configurable through source code
- Basic EWMH compliance
FAQ (Quick Reference)
How do I change the terminal?
Edit src/config.hpp:
static const char *termcmd[] = { "alacritty", NULL };
Then recompile.
How do I change the modifier key?
Edit src/config.hpp:
#define MODKEY Mod1Mask // For Alt key
How do I add more workspaces?
Edit src/nwm.hpp:
#define NUM_WORKSPACES 12
Then add labels and keybindings in src/config.hpp.
Can I use NWM on Wayland?
No, NWM is an X11 window manager and requires Xorg.
Why no multi-monitor support?
Multi-monitor support is complex and not currently a priority. You can use xrandr to combine monitors into one large screen.
How do I set a wallpaper?
Use feh or nitrogen:
feh --bg-fill ~/Pictures/wallpaper.jpg
Add to ~/.xinitrc for persistence.
Why do I need to recompile for configuration changes?
This is the suckless philosophy: configuration is code. It ensures:
- Type safety
- No runtime parsing overhead
- Full power of C++
- Explicit configuration
- Forces you to understand what you're changing
Is NWM suitable for beginners?
NWM is relatively simple compared to many window managers, but it does require:
- Comfort with command line
- Basic understanding of X11
- Willingness to edit C++ code
- Ability to compile from source
If you've never used a tiling window manager, you might want to start with i3 (no compilation needed) then try NWM.
Where can I find example configurations?
Check:
- The default
src/config.hpp - GitHub issues for user-shared configs
- r/unixporn posts using NWM
Contact
- GitHub: https://github.com/xsoder/nwm
- Issues: https://github.com/xsoder/nwm/issues
- Author: xsoder
—
Thank you for using NWM!
If you find NWM useful, please star the repository on GitHub and share it with others who might be interested in a clean, hackable tiling window manager.