swim - draft specification for a simple, extensible window manager

2021-03-07

swim proposal v0.0.1

This document is a draft proposal for a new window manager, swim.

swim (the Simple WIndow Manager) is a simple, extensible window manager built for Wayland. It aims to implement only a minimal, unopinionated feature set, with extensible interfaces for custom window layouts and other behaviour. The intention is that swim should provide only those features required by all window managers, upon which arbitrary layout algorithms and custom behaviour can be implemented.

At its core, swim assigns workspaces (a representation of a display) to physical or virtual displays, per seat. The visible windows on each workspace are determined by a system of tags. The position of each window is determined entirely by external layouts that are bound to the workspace. Layouts also expose additional commands to control window position.

A system of event propagation exists to allow input events to be intercepted and trigger associated commmands. Events originate either in the seat, or according to custom program logic, and propate in sequence through the active display, workspace, layouts, and active window.

Event-command bindings exist at the seat and workspace levels to control different aspects of the window manager. Bindings within the seat reference commands within the active display and workspace, whereas bindings within the layout reference only commands exposed by the layout. For example, commands to control the currently active display are bound within the seat, whereas commands to cycle window position may be bound within the workspace.

The user can affect the behaviour of swim according to two kinds of extension:

These may be implemented either as libraries that link against swim, or instead as wayland clients that communicate with swim via a protocol extension. Implementation details such as these are still to be determined.

Layouts

Layouts manage on-screen window positions. They must expose the following methods:

The implementation must be according to the following behaviour:

A layout must act as if it contains the windows it is given. It must only use the part of the screen it is told to use. It will be told when it needs to start managing a window, and when it should stop. It must be able to report where all of its visible windows should be rendered. This means that it is possible for a layout to create more layouts and delegate window management to them. Where nested layouts are required, the visible windows of the parent layout should be the visible windows of its visible sublayouts. This approach enables us to re-use logic: e.g. the stack layout could be implemented as nested hsplit layouts.

Layout commands

Alongside the methods that must be implemented, a layout can optionally define additional commands to manipulate the state of the layout. These commands can be bound to events within swim workspaces, to run them in response to events such as key combinations. Examples of layout commands might include re-ordering the internal structure of windows, temporarily hiding windows, etc.

Nested layouts should include sufficient commands to control their sub-layouts.

Workspaces

A workspace is a representation of a display which can be bound to either a physical or a virtual display in the seat, and itself binds events to layout commands.

Workspaces have a given resolution, and call the visible_windows method on its layout to determine window position.

Tags

Each workspace maintains a set of tags, where each tag is a string. Each window also maintains a set of tags of size >=1.

The layout bound to the workspace is instructed to draw the set of windows whose tags match those of the layout instance.

Events

Events are signals that result in commands being called. They are propagated through the following chain:

seat -> active_physical_display -> workspace -> layouts -> active_windows

An event may begin as a keyboard/mouse input of a given seat, or instead according to program logic within the workspace.

Within the seat, events can be mapped to commands for switching focus to different displays, etc.

Within the workspace, events can be mapped to the commands implemented by the underlying layout.

At the levels of seat, and workspace, a mapping can be configured between events and commands. The seat implements commands for switching focus to different displays. The layout implements commands, as aforementioned, for changing window order etc.

Plugins

Plugins allow swim to be augmented with custom behaviour, according to swim's public interface. Plugins are expected to provide non-blocking initialisation and destruction commands

Plugins may implement systems such as emitting events (such as handling keyboard and mouse input), intercepting screen drawing (such as screencasting to a certain URL, or applying a colour filter to a workspace), etc. They can also implement methods for manipulating layou

swim provides a set of hooks that can be used by plugins to respond to changes in state (e.g. the addition/removal of physical displays, the lock signal). swim implements methods to bind/unbind callbacks to the hooks.

Plugins must provide commands for initialisation/destruction, which may be called by default or in response to events. They can define additional commands for custom logic and controlling the state of the plugin at runtime. Plugins may require plugin-specific config, which may be passed to the plugin object on initialisation.

All plugin methods are expected to be non-blocking: they register the user's intention, and hand control back to the main thread before performing the required actions. Initialisation and destruction must be idempotent.

Hooks

Hooks are handles which plugins can use to be notified of state changes within swim. They come in in three forms: *_added, *_removed, and *_changed. Examples may include workspace_changed, display_added, etc.

Callbacks are registered to hooks using hook/unhook methods. The hook method expects a callback and returns an ID that can be later passed to unhook to detatch the callback.

The implementation of hooks is somewhat subltle, because it needs to deal with cases such as:

Still to consider

Desktop backgrounds: a layout, or part of the workspace? Title bars Starting sessions without seats Attaching to remote sessions Adding/removing seats to/from an existing session. This allows locking to be implemented by detatching a workspace from a display and attaching the lockscreen workspace instead. Meanwhile, remote sessions and plugins such as screen recorders keep working.

Design Principles

All core swim methods should be non-blocking. Blocking operations should be performed by notifying the main thread to call a callback soon. The callback handles the blocking operation.

Articles from blogs I read

In praise of Plan 9

Plan 9 is an operating system designed by Bell Labs. It’s the OS they wrote after Unix, with the benefit of hindsight. It is the most interesting operating system that you’ve never heard of, and, in my opinion, the best operating system design to date. Even …

via Drew DeVault's blog November 12, 2022

Do I still remember how to blog?

I haven’t written a blog post for a couple of months now, which is a good indicator I should probably document my workflow before I forget how to do it…

via Not Just Serendipity October 9, 2022

Github Copilot: The future of programming?

My thoughts on GitHub Copilot and the future of AI assistants in writing code.

via Josh Smailes April 15, 2022

Generated by openring