Skip to content

yazeed1s/zwm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

295 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zwm

About ZWM

zwm is a minimalistic and opinionated tiling window manager for X11. It uses XCB instead of Xlib to communicate with the X server. The underlying data structure for managing windows is a customized BSP tree.

Features

  • Compliance with a subset of ewmh and icccm
  • Multiple layouts: default, master, stack, grid, monocle, three-column, and deck.
  • Multiple virtual desktops.
  • Multi-monitor support.
  • Independent workspaces for each monitor by default.
  • Low memory footprint, runs within ~2MB of memory
  • Resize, flip, and swap windows or partitions.
  • Layouts apply to individual desktops.
  • Keyboard-driven, fully controlled via keyboard shortcuts.
  • Mouse for convenience when move/resize for floating/tiled windows and partitions.
  • Drag-and-drop windows between partitions.
  • Customizable settings.
  • Customizable window rules.
  • Can be integrated with any status bar.
  • Can handle multiple bars per monitor/display, in different locations (top, bottom, left, right).
  • Config reload on the fly.
  • And many more.. look at they keybinds section to see what zwm offers.

The underlying data structure:

ZWM uses binary space partitioning tree (BSP-tree) to store and manage windows.

  • Each desktop has its own pointer to a bsp-tree.
  • The tree is a partition of a monitor's rectangle into smaller rectangular regions.
  • Each leaf node holds exactly one window.
  • Each node in a bsp-tree either has zero or two children.
  • Each internal node is responsible for splitting a rectangle in half.

The following should explain how bsp-tree is used to achieve window management:

    Window and Partition Structure in a BSP-tree:

    The layout of windows in BSPWM follows a binary tree structure:

         I         ROOT (root is an INTERNAL NODE, unless it is a leaf by definition)
       /   \
      I     I      INTERNAL NODES (screen sections/partitions)
     / \   / \
    E   E E   E    EXTERNAL NODES (windows/leaves)

    - Internal Nodes (I) represent screen sections where windows can be displayed.
    - External Nodes (E) represent the actual windows within those sections.
    - Windows (E nodes) share the width, height, and coordinates (x, y) of their   parent sections (I nodes).

    Example Structure:

         I
       /   \
      I     I
     / \   / \
    E   E E   I
             / \
            E   E

    Partition Behavior:
    - Internal nodes (I) can contain other partitions or be contained within other partitions.
    - External nodes (E) are the leaves (actual windows).

    Example of tree:
    - 1, 2, 3 are leaves/windows (EXTERNAL_NODE).
    - a, b are internal nodes (INTERNAL_NODE), or screen sections/partitions.

            1                               a                          a
                                           / \                        / \
                            --->          1   2         --->         1   b
                                                                        / \
                                                                       2   3
    Visualization of Screen Layout:

    +-----------------------+  +-----------------------+  +-----------------------+
    |                       |  |           |           |  |           |           |
    |                       |  |           |           |  |           |     2     |
    |                       |  |           |           |  |           |           |
    |           1           |  |     1     |     2     |  |     1     |-----------|
    |                       |  |           |           |  |           |           |
    |                       |  |           |           |  |           |     3     |
    |                       |  |           |           |  |           |           |
    +-----------------------+  +-----------------------+  +-----------------------+

    Another Example:
    - Numbers are the are leaves/windows (EXTERNAL_NODE).
    - Letters are internal nodes (INTERNAL_NODE), or screen sections/partitions.

             a                             a                          a
            / \                           / \                        / \
           1   b            --->         c   b         --->         c   b
              / \                       / \ / \                    / \ / \
             2   3                     1  4 2  3                  1  d 2  3
                                                             	    / \
                                                                   4   5

     +-----------------------+  +-----------------------+  +-----------------------+
     |           |           |  |           |           |  |           |           |
     |           |     2     |  |     1     |     2     |  |     1     |     2     |
     |           |           |  |           |           |  |           |           |
     |     1     |-----------|  |-----------|-----------|  |-----------|-----------|
     |           |           |  |           |           |  |     |     |           |
     |           |     3     |  |     4     |     3     |  |  4  |  5  |     3     |
     |           |           |  |           |           |  |     |     |           |
     +-----------------------+  +-----------------------+  +-----------------------+

Screenshots:

Installation

Arch Linux (AUR)

yay -S zwm

Void Linux (XBPS)

xbps-install -S zwm

Build from source

Dependencies
  • gcc
  • libxcb
  • xcb-util
  • xcb-util-keysyms
  • xcb-util-wm (ewmh,icccm)
  • lxcb-randr
  • lxcb-xinerama
  • lxcb-cursor
git clone https://github.com/Yazeed1s/zwm.git
cd zwm && sudo make install

Configuration

When you first start zwm, a config file will be generated in the following location with the default config.

  • ~/.config/zwm/zwm.conf
As of now, the following configs are offered:

1- Config variables

border_width = 2
active_border_color = 0x4a4a48
normal_border_color = 0x30302f
window_gap = 10
virtual_desktops = 7
focus_follow_pointer = true
focus_follow_spawn = false
restore_last_focus = false
Available Variables:
  • border_width: Defines the width of the window borders in pixels.
  • active_border_color: Specifies the color of the border for the active (focused) window.
  • normal_border_color: Specifies the color of the border for inactive (unfocused) windows.
  • window_gap: Sets the gap between windows in pixels.
  • virtual_desktops: sets the number of virtual desktops.
  • focus_follow_pointer: If false, the window is focused on click; if true, the window is focused when the cursor enters it.
  • focus_follow_spawn: If false, new windows require manual focus (e.g., via click); if true, newly spawned windows will automatically receive focus.
  • restore_last_focus: If true, ZWM will restore the previously focused window when switching to a desktop, only if that desktop’s layout is not set to stack.

Layouts

  • default: Manual BSP tiling. New windows split the focused partition.
  • master: One selected master window with the remaining windows arranged in the stack area.
  • stack: All tiled windows occupy the same rectangle; use stack traversal to cycle focus.
  • grid: Tiled windows are distributed into an automatic grid.
  • monocle: One tiled window is visible at a time. Other tiled windows are unmapped; floating windows remain visible.
  • three_col: The window under the cursor becomes the center column when the layout is selected. Other tiled windows are split between the left and right columns. The center column can be resized with resize:grow/resize:shrink or mouse resize.
  • deck: The window under the cursor becomes the left master when the layout is selected. Other tiled windows share the right deck area; stack traversal cycles only through the deck windows. The master/deck split can be resized with resize:grow/resize:shrink or mouse resize.

2- Commands to run on startup

Use the exec directive to specify programs that should be started when ZWM is launched.
  • For a single command:
exec = "polybar"
  • To specify additional arguments as a list:
exec = ["polybar", "-c", ".config/polybar/config.ini"]

3- Custom window rules

Custom window rules allow you to define specific behaviors for windows based on their window class.
Syntax:
rule = wm_class("window class name"), state(tiled|floated), desktop(1..N)
Explanation:
  • wm_class: The window class name used to identify the window.
    • Use the xprop tool to find the wm_class of a window.
  • state: Specifies whether the window should be tiled or floated.
  • tiled: The window will be tiled... clearly.
  • floated: The window will be floated... clearly.
  • desktop: The virtual desktop number where the window should be placed.
    • Use -1 if you do not want to set it to a specific desktop.
; Example:
rule = wm_class("firefox"), state(tiled), desktop(-1)
; This rule sets "firefox" window to be tiled and does not change its virtual desktop.

4- Key bindings

  • The format for defining key bindings is: bind = modifier + key -> action
  • If two modifiers are used, combine them with a pipe (|). For example, alt + shift is written as alt|shift.
  • Note: Some functions require additional arguments to specify details of the action.
  • These arguments are provided using a colon syntax, where the function and its argument are separated by a colon.
  • Example: func(switch_desktop:1) means "switch to desktop 1".
  • Example: func(resize:grow) means "grow the size of the window".
  • Example: func(layout:master) means "toggle master layout".

Available modifiers:

  • super (meta), alt, shift, ctrl

Available Actions:

  • run(...): Executes a specified process.

    • Example: bind = super + return -> run("alacritty")
    • To run a process with arguments, use a list: Example: bind = super + p -> run(["rofi", "-show", "drun"])
  • func(...): Calls a predefined function. The following functions are available:

    • kill: Kills the focused window.
    • switch_desktop: Switches to a specified virtual desktop.
    • fullscreen: Toggles fullscreen mode for the focused window.
    • swap: Swaps the focused window with its sibling.
    • transfer_node: Moves the focused window to another virtual desktop.
    • layout: Toggles the specified layout (master, default, stack, grid, monocle, three_col, deck).
    • traverse: In stack/monocle/deck layouts, moves focus to the previous or next stacked window. In deck, traversal skips the master and cycles through the deck area.
    • flip: Changes the window's orientation; if the window is primarily vertical, it becomes horizontal, and vice versa.
    • cycle_window: Moves focus to the window in the specified direction (up, down, left, right).
    • cycle_desktop: Cycles through the virtual desktops (left, right).
    • resize: Adjusts the size of the focused window (grow, shrink).
    • reload_config: Reloads the configuration file without restarting ZWM.
    • shift_window: Shift the floating window's position to the specified direction by 10px (up, down, left, right).
    • gap_handler: Increase or decrease window gaps (GROW, SHRINK).
    • change_state: Set window state (FLOATING, TILED).
    • grow_floating_window: Grow floating window (horizontally or vertically).
    • shrink_floating_window: Shrink floating window (horizontally or vertically).
    • cycle_monitors: Cycle between monitors (left or right, relative to the linked-list order not the physical positioning).
    • start_keyboard_drag: Enter keyboard-driven drag mode to move tiled windows between partitions.
  • Default keys

bind = super + return -> run("alacritty")
bind = super + space -> run("dmenu_run")
bind = super + p -> run(["rofi", "-show", "drun"])
bind = super + w -> func(kill)
bind = super + 1 -> func(switch_desktop:1)
bind = super + 2 -> func(switch_desktop:2)
bind = super + 3 -> func(switch_desktop:3)
bind = super + 4 -> func(switch_desktop:4)
bind = super + 5 -> func(switch_desktop:5)
bind = super + 6 -> func(switch_desktop:6)
bind = super + 7 -> func(switch_desktop:7)
bind = super + l -> func(resize:grow)
bind = super + h -> func(resize:shrink)
bind = super + i -> func(gap_handler:grow)
bind = super + d -> func(gap_handler:shrink)
bind = super + f -> func(fullscreen)
bind = super + s -> func(swap)
bind = super + up -> func(cycle_window:up)
bind = super + right -> func(cycle_window:right)
bind = super + left -> func(cycle_window:left)
bind = super + down -> func(cycle_window:down)
bind = super|shift + up -> func(shift_window:up)
bind = super|shift + right -> func(shift_window:right)
bind = super|shift + left -> func(shift_window:left)
bind = super|shift + down -> func(shift_window:down)
bind = super|alt + f -> func(change_state:float)
bind = super|alt + t -> func(change_state:tile)
bind = super|shift + t -> func(shrink_floating_window:horizontal)
bind = super|shift + g -> func(shrink_floating_window:vertical)
bind = super|shift + y -> func(grow_floating_window:horizontal)
bind = super|shift + h -> func(grow_floating_window:vertical)
bind = super|alt + left -> func(cycle_desktop:left)
bind = super|alt + right -> func(cycle_desktop:right)
bind = super|ctrl + right -> func(cycle_monitors:next)
bind = super|ctrl + left -> func(cycle_monitors:prev)
bind = super|shift + 1 -> func(transfer_node:1)
bind = super|shift + 2 -> func(transfer_node:2)
bind = super|shift + 3 -> func(transfer_node:3)
bind = super|shift + 4 -> func(transfer_node:4)
bind = super|shift + 5 -> func(transfer_node:5)
bind = super|shift + 6 -> func(transfer_node:6)
bind = super|shift + 7 -> func(transfer_node:7)
bind = super|shift + m -> func(layout:master)
bind = super|shift + s -> func(layout:stack)
bind = super|shift + d -> func(layout:default)
bind = super|shift + c -> func(layout:grid)
bind = super|shift + o -> func(layout:monocle)
bind = super|shift + x -> func(layout:three_col)
bind = super|shift + v -> func(layout:deck)
bind = super|shift + k -> func(traverse:up)
bind = super|shift + j -> func(traverse:down)
bind = super|shift + f -> func(flip)
bind = super + m -> func(start_keyboard_drag)
bind = super|shift + r -> func(reload_config)

More options will be added in the future as development progresses.

Default Keybindings

Key Description
super + w kill/close window
super + return launch a terminal (alacritty)
super + space launch dmenu
super + p launch rofi
super + [1..N] switch to desktop
super + l resize window (grow/expand)
super + h resize window (shrink)
super + f toggle fullscreen
super + shift + [1..N] transfer window to a diff desktop
super + shift + m toggle master layout
super + shift + s toggle stack layout
super + shift + d toggle default layout
super + shift + c toggle grid layout
super + shift + o toggle monocle layout
super + shift + x toggle three-column layout
super + shift + v toggle deck layout
super + shift + j/k traverse the stack
super + shift + f flip the window/partition
super + shift + r hot-reload
super + shift + y grow floating windows horizontally
super + shift + h grow floating windows vertically
super + shift + t shrink floating windows horizontally
super + shift + g shrink floating windows vertically
super + button1 move window (drag tiled / move floating)
super + button3 resize window (drag resize)
super + ctrl + → focus/change monitor right
super + ctrl + ← focus/change monitor left
super + s swap window's orientation
super + ← focus window on the left
super + ↑ focus window above
super + → focus window on the right
super + ↓ focus window below
super + alt + → cycle desktop right
super + alt + ← cycle desktop left
super + shift + ← shift floating window to the left by 10px
super + shift + ↑ shift floating window up by 10px
super + shift + → shift floating window to the right by 10px
super + shift + ↓ shift floating window down by 10px
super + i increase window gaps by 5px
super + d decrease window gaps by 5px
super + alt + t tile window
super + alt + f float window
super + m start keyboard-driven window drag

Mouse and Keyboard Control

ZWM is written in a way to be fully keyboard-driven but it also offers mouse support for convenience.

Drag and resize features

Explained below are the operations that can be performed via both mouse and keyboard.

1. Move/drag window

  • Mouse: super + button1 (Left Click) while dragging the window.
    • Works on Tiled Windows: Drags the window to a new partition (swaps or moves).
    • Works on Floating Windows: Moves the window freely.
  • Keyboard:
    • Tiled: super + m initiates keyboard drag mode. Use arrow keys to move the window.
    • Floating: super + shift + [arrow keys] shifts the window to the arrow direction by 10px.

2. Resize window

  • Mouse: super + button3 (Right Click) while dragging near edges/corners or layout split lines.
    • Works on Tiled Windows in the default layout: Resizes the shared edge between windows.
    • Works on Deck and Three-column layouts: Resizes the main split.
    • Works on Floating Windows: Resizes the window dimensions.
  • Keyboard:
    • Tiled: super + l (Grow) / super + h (Shrink).
    • Floating: super + shift + y/h (Grow Horiz/Vert) and super + shift + t/g (Shrink Horiz/Vert).

ewmh specific settings for polybar

To display the window name (CLASS_NAME):

[module/xwindow]
type 			= internal/xwindow
format 		= <label>
; Available tokens:
;   %title%
;   %instance% (first part of the WM_CLASS atom, new in version 3.7.0)
;   %class%    (second part of the WM_CLASS atom, new in version 3.7.0)
; Default: %title%
label 		    = %title%
label-maxlen 	= 50
label-empty 	= "[null]"

To display workspaces:

[module/ewmh]
type = internal/xworkspaces
label-active 			        = %index%
label-active-background 	= ${colors.bg}
label-active-underline		= ${colors.blue}
label-active-padding		  = 1
label-occupied 			      = %index%
label-occupied-padding 		= 1
label-urgent 			        = %index%!
label-urgent-background 	= ${colors.red}
label-urgent-padding 		  = 1
label-empty 			        = %index%
label-empty-foreground 		= ${colors.gray}
label-empty-padding 		  = 1
label-separator 		      = " "

For further customization please refer to polybar's wiki.

Contributing

If you would like to add a feature or to fix a bug please feel free to submit a PR.. after we discuss in an Issue.

About

X11 tiling window manager

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages