Users pulling this repo directly from the mainline branch will see this as a breaking change
See here for details.
The docs for penrose are written using mdBook and published to GitHub Pages here. They cover some more general concepts about how to get up and running as opposed to the crate docs on docs.rs which are more aimed at covering the APIs themselves.
This means that, unlike most other tiling window managers, Penrose is not a
binary that you install on your system. Instead, you use it like a normal
dependency in your own crate for writing your own window manager. Don't worry,
the top level API is well documented and a lot of things will work out of the
box, and if you fancy digging deeper you'll find lots of opportunities to
customise things to your liking.
Penrose was born out of my failed attempts to refactor the dwm codebase into
something that I could more easily understand and hack on. While I very much
admire and aim for minimalism in code, it becomes a problem when your complex
code base starts playing code golf to keep things under an arbitrary line limit.
I won't claim that Penrose has the cleanest code base you've ever seen, but it
should be readible in addition to being fast. If something is confusing or
unclear, then I count that as a bug (and please raise it as such!)
I've also tried my hand at Xmonad in the past. I love the set-ups people can achive with it (this one is a personal favourite) but doing everything in Haskell was a deal breaker for me. I'm sure many people will say the same thing about Rust but then at least I'm giving you some more options!
With Penrose, a simple config can be written in about 5 minutes and roughly 50
lines of code. It will be pretty minimal but each additional feature (such as a
status bar, scratch-pads, custom layouts, dynamic menus...) can be added in as
little as a single line. If the functionality you want isn't available however
then that leads us on to...
dwm patches, qtile lazy APIs, i3 IPC configuration; all of these definitely work but they are not what I'm after. Again, the Xmonad model of companion libraries that you bring in and use as part of writing your own window manager has always felt like the right model for me for extending the window manager. (Though, again, while Haskell is great fun for tinkering I've never felt productive in it)
Penrose provides a set of Rust traits for defining the various ways you can
interact with the main WindowManager struct. You are free to write your own
implementations, write code that manipulates them and extend them however you
see fit. If you want to check out some examples of what is possible, take a look
in the contrib directory.
Want to run some particular logic every time you connect external monitors? Write a hook that listens for randr triggers.
Want to scatter your windows at random over the screen? Write a custom layout and make use of all of the helper methods on regions.
Have an idea that you can't currently implement? Raise an issue and suggest an extension to the API!
Parsing a config file and dynamically switching behaviour on the contents adds a
huge amount of complexity to the code: not to mention the need for validating
the config file! By default, Penrose is configured statically in your
main.rs and compiled each time you want to make changes (similar to
Xmonad and dwm).
That said, the extensibility of Penrose means that
you are free to define your own config file format and parse that as part of
your startup, if that is something you want. You could read from xresources,
or a stand alone file of your design.
The choice is yours!
There are several places where Penrose makes use of external programs for
utility functionality (reading the user's key-map or spawning a program launcher
for example), but core window manager functionality is always going to stay
internal.
As you might expect, you can definitely write your own extensions that provide
some sort of IPC or client/server style mechanism if you want to mirror the
kinds of things possible in other window managers such as i3 or bspwm, but
that is not going to be included at the expense of statically defined control in
your binary as a default.