logo
Expand description

Penrose: a library for building your very own tiling window manager

Penrose is inspired by similar projects such as dwm, xmonad and qtile which allow you to configure your window manager in code and compile it for your system. It is most similar to xmonad in that it is more of a library for building a window manager (with low level details taken care of for you) rather than a minimal window manager that you edit and patch directly (such as dwm). Penrose strives to be as simple as possible in its implementation in order to make the guts of the window manager easier to understand. Given the nature of what this involves, this is not always possible but effort has been made to make the source readable and with relatively little magic.

Using Penrose

Penrose itself is not a binary application that you can build, install and run. You need to write your own main.rs as a rust binary crate that uses Penrose to set up, configure and run your very own window manager exactly how you want it. In short, you will need to write some code and you will need to know rust to some degree.

For learning rust itself, there are some fantastic official guides available on rust-lang.org and if you are sticking to using the out of the box functionality provided by the penrose crate, working through the book before diving into Penrose should be more than enough to get you started.

On GitHub you can find up to date examples of how to set up and configure penrose as your window manager, ranging from bare bones minimal to custom extensions and hooks.

Getting started

At it’s simplest you will need to create a new binary crate to build your window manager and add penrose as a project dependency:

$ cargo new –bin my_penrose_config

As a bare minimum, you will need to the following in your main.rs:

  • keybindings (typically set up using the gen_keybindings macro)
  • A XConn instance to handle communication with the X server
  • A Config instance which contains the rest of your top level configuration for Penrose. Things like workspace names, layout functions and settings for gaps and borders.

With that, you will be able to create a WindowManager and start running Penrose after building and installing your binary. (It is also suggested that you set up a logging handler so that debugging any issues with your config is easier. simplelog is a good choice if you are unsure where to start with this.)

Example

 #[macro_use]
 extern crate penrose;

 use penrose::{
     core::helpers::index_selectors,
     logging_error_handler,
     xcb::new_xcb_backed_window_manager,
     Backward, Config, Forward, Less, More, WindowManager
 };

 fn main() -> penrose::Result<()> {
     let key_bindings = gen_keybindings! {
         "M-j" => run_internal!(cycle_client, Forward);
         "M-k" => run_internal!(cycle_client, Backward);
         "M-S-j" => run_internal!(drag_client, Forward);
         "M-S-k" => run_internal!(drag_client, Backward);
         "M-S-q" => run_internal!(kill_client);
         "M-Tab" => run_internal!(toggle_workspace);
         "M-grave" => run_internal!(cycle_layout, Forward);
         "M-S-grave" => run_internal!(cycle_layout, Backward);
         "M-A-Up" => run_internal!(update_max_main, More);
         "M-A-Down" => run_internal!(update_max_main, Less);
         "M-A-Right" => run_internal!(update_main_ratio, More);
         "M-A-Left" => run_internal!(update_main_ratio, Less);
         "M-semicolon" => run_external!("dmenu_run");
         "M-Return" => run_external!("alacritty");
         "M-A-Escape" => run_internal!(exit);

         map: { "1", "2", "3", "4", "5", "6", "7", "8", "9" } to index_selectors(9) => {
             "M-{}" => focus_workspace (REF);
             "M-S-{}" => client_to_workspace (REF);
         };
     };

     let mut wm = new_xcb_backed_window_manager(
         Config::default(),
         vec![],
         logging_error_handler()
     )?;
     wm.grab_keys_and_run(key_bindings, map!{})
 }

Digging into the API

To add more functionality and flexability, you can start to add things like Hooks, a status bar and custom actions for running as part of key bindings. You will want to read the documentation of the core module which contains all of the core functionality of Penrose as a window manager. After that, the draw module contains utilities for rendering things like status bars and widgets, the contrib module has examples of simple hooks, extensions and key binding actions and the xcb module contains the referencing trait implementations for interacting with the X server via the XCB api.

NOTE: in order to use the xcb implementation of penrose, you will need to install the C libraries that are dependencies (namely xcb, Cairo and Pango).

Re-exports

pub use crate::core::data_types::Change::*;
pub use crate::core::ring::Direction::*;

Modules

Extensions and additional functionality for penrose

Core functionality for the Penrose window manager library

Traits and utilities for rendering custom windows

Helpers and utilities for using XCB as a back end for penrose

Macros

Generate user keybindings with optional compile time validation.

Make creating all of the mouse bindings less verbose

Make creating a HashMap a little less verbose

Quickly create a simple string error

kick off an external program as part of a key/mouse binding.

Kick off an internal method on the window manager as part of a key binding

Helper for spawning external processes and ignoring the output

Helper for spawning external processes and capturing the output

Structs

The main user facing configuration details.

WindowManager is the primary struct / owner of the event loop for penrose.

Handles communication with an X server via the XCB library.

Enums

Where a given element should be inserted into a Ring

Enum to store the various ways that operations can fail in Penrose

Used with WindowManager helper functions to select an element from the known workspaces or clients.

Functions

A simple error handler that just logs the error to the penrose log stream

Construct a penrose WindowManager backed by the default xcb backend.

Type Definitions

A function that can be registered to handle errors that occur during WindowManager operation

Top level penrose Result type

An X resource ID