User Tools

Site Tools


winterbreeze

Winterbreeze

In the winterbreeze branch, we evolve herbstluftwm into a modern, object-oriented design. Development takes place here on github.

This effort started with the idea of porting hlwm to C++ and removing the Glib dependency. While hlwm master currently is already C++ code, it is not fundamentally different from a typical C program. E.g., it uses global variables, passes around function pointers and does not follow any basic OOP principles.

Maintainers

The branch is maintained by Thorsten and ypnos (Johannes). You can discuss with us on IRC and via the mailing list.

Any input on the design, proposals, or the offer to work with us on the branch is always welcome.

Vision

A window manager is, like most GUI-related systems, inherently object-oriented. Herbstluftwm brings some great innovations and does several other things just the “right way” from the user point of view. However, internally, it has grown out of its original plain C software design. For example, the object system that allows inspection and manipulation of the window manager's behavior from outside, was added later on in the game. It is a great help to users, but a burden to the programmer. Many design choices are hardwired into herbstluftwm. For example, a fixed set of hooks exist and care has to be taken to trigger a certain hook at the specific place in the code.

In winterbreeze, the object system becomes a first citizen in the herbstluftwm software design. This is just natural, given the object-oriented software model that comes with C++. It allows us to abstract all the overhead of objects, hooks, etc. away from the core components. Furthermore, it solves the problem of how different modules of herbstluftwm interact. Currently, they are very interwoven, and most source files include pretty much every other module's header. In winterbreeze, the object tree can also be used internally to pass information along or trigger actions. The object tree serves as a global interface between modules.

Goals & Principles

Here is a short overview of what we aim for:

  • Extensive use of C++11 language features. The new features lead to cleaner code which also benefits unexperienced developers.
  • Use of C++11 data types and containers instead of a toolkit.
  • Sensible object-oriented design.
  • Object tree at the core of hlwm. We avoid redundant structures by incorporating the externally-accessible object tree into the internal object model.
  • Use of shared pointers when handling clients, tags, etc. to avoid segmentation faults. An inconsistent state in the wm like dangling entities is better than the wm crash.
  • Backwards-compatibility. The external behavior and interface are regarded as sound and should typically stay the way it is. We keep the focus on an internal cleanup.

Here is what we don't care about:

  • Performance. It is generally not a concern in the hlwm development right now.
  • New features. While the new design opens the door for some new features, they are only introduced when they become implicit.

Design

Object Tree

Let's have a look at the classes first:

Entity ← Attribute ← Attribute_<T>
                   ← DynamicAttribute
       ← Action
       ← Directory ← Root
                   ← Object ← Hook
                            ← ClientManager
                            ← HookManager

As you can see, basically everything that will end up in the object tree (i.e., externally accessible through the tree) is derived from Entity. The Entity itself doesn't do much, but it always has a name and a type that can be printed. We will walk through all the derived classes in a minute. Let's talk about the Root class first, though. It is a singleton and it is global in a sense that it can always be fetched by calling Root::get(). The root node holds hlwm modules and can be used internally to obtain them. Right now we have modules that manage clients and hooks. So this is how the tree looks like:

Root → HookManager → Hook 1
                   → Hook 2
                   → …
     → ClientManager → Client 1
                     → Client 2
                     → …

The list will go on, with TagManager, MonitorManager etc.

Attributes and Actions

Each Object can hold certain attributes. An important concept is that attributes are dumb. Whenever they are accessed externally, their owner will process the access. E.g., when an attribute, even a static one, is read through the object tree interface, it is passing the request to its owner; which then typically just returns the value, but might also do something else.

Static Attributes

These are member variables of their owning Object, but they are not just simple types like int or Color, but Attribute_<int>, Attribute_<Color> and so on. This makes them accessible through the object tree for reading and writing. It also makes them hookable. That means, whenever the attribute is changed, a hook pointing to that attribute will be notified. Hooks are covered later.

Dynamic Attributes and Actions

In the previous object tree design (master branch), some attributes would be special.

The first case is dynamic variables. For example, an attribute count might not return a stored value, but be computed on demand. This is what DynamicAttribute is for. It is a shallow object; the owner of the attribute needs to provide the answer to the inquiry. There is also a possible case for a dynamic writeable attribute.

The second case is triggers. Now, in the previous design, the user could “write” a value into certain attributes to let some magic happen. We change this to the explicit Action type. An action can be triggered with optional arguments.

Directories and Objects

A Directory is an Entity that has children, which are also directories. It does not have attributes or actions. An Object is a Directory that also can also hold attributes and actions.

Directories (and therefore, also objects) have the important property that they are always held by shared pointers (std::shared_ptr). This means they can never disappear while being referenced. They are also aware of being held by a shared pointer and derived from std::enable_shared_from_this<Directory>.

An example use for this is self-passing of the object to a hook (as explained later) watching on it. The hook does not keep shared pointers though, it uses std::weak_ptr instead. This is how you should hold a directory/object that you don't want to keep alive. In the hook scenario, an object should not be prevented from being cleaned up (e.g., a tag being removed) just because a hook is watching on it. On the contrary, a tag should not disappear as long as there are clients on it.

Hooks

Previously, hooks were hardwired additions to the code like the object tree. Because we make the object tree inherent to the herbstluftwm design, we can instead exploit the object tree for hooks. The objects themselves (e.g. TagManager and Tag) do not explicitely handle hooks in the typical case. So in our example, adding or removing tags, or changing a tags attribute, can just be done without taking care of any hooks. The second change is that because of this, basically (almost) every directory and attribute (or action) is hookable.

Hook is derived from Object, so that the hooks themselves can be inspected and externally triggered through the object tree.

A hook is identified by its name, which is the full path of a directory or attribute/action. If the hook points to a directory, it will notify of additions or removals of children. For example, if you need to watch focus changes, you can hook into clients.focus.name. To watch for added or removed tags, you watch either tags or tags.by-name. In the latter case, you will get notified of the name of the new (or removed tag), while in the former case, you will get notified of index changes (which are typically less helpful).

A HookManager is responsible for adding and removing hooks, which can be triggered through the object tree using the respective actions. So the user will issue:

hooks.add clients.focus.name

While we now gain the power to define custom hooks and watch them, no special interface is introduced to manage hooks. The object tree can be used for all kinds of operations this way, removing the need to define tailored commands.

Hooks can also be externally triggered using their emit action, with custom arguments. A custom hook that is not in relation to any real directories or attributes therefore can be called, for example, user.foo. This basically resembles the old emit_hook command, but makes custom hooks explicit. It could make that command obsolete, but right now remaining backwards-compatibility is one of our goals.

Clients

We are current working on this component.

winterbreeze.txt · Last modified: 2015/02/25 01:01 by ypnos