Bspwm: Monocle mode for any leaf node

Created on 7 Dec 2016  路  15Comments  路  Source: baskerville/bspwm

Wasn't exactly sure how to title this.

In KWM on macOS, which seems to have a lot in common with bspwm, has a feature where you can turn any leaf node of the tree into a monocle node, and it allows you to stack windows in that node. There's a corresponding command and keybinding to cycle through the nodes in a stack if you're focused on a monocle node. Sort of like i3's stacking mode.

Is something like this possible (to add/implement) in bspwm?

Most helpful comment

Being able to have a specific node in a stacked/monocle/tabbed mode is the one feature which currently holds me back from using bspwm. Otherwise, I think conceptually bspwm is the best wm out there. I often have an editor on one side which always needs to be visible and other programs stacked on each other on the other side (firefox, pdf-reader, other programs which briefly pop up) and I need to rotate between the stacked programs. Like the tabbed layout in i3 or the deck-patch in dwm (https://dwm.suckless.org/patches/deck/).

All 15 comments

Just from my causal perusing of <bspc wm -d> it appears that the tiling vs monocle is hardcoded into desktops and not nodes, so this would not be trivial. That said it certainly is possible.

If you really like this feature and want something similar to bspwm you could try herbstluftwm. Basically it has an additional layer named frames, it has monitors > tags (a variant of desktops) (manual tiling layout of frames) > frames (horizontal, vertical, max, and grid layouts) > windows. However I had trouble with using floating windows in it, it started to segfault on start (probably my fault somehow), uses Xlib instead of XCB, uses C++ instead of C, and it seems less active, but it does seem quite popular nevertheless.

The same could in theory be achieved in bspwm, even in its current form. You could misuse monitors to create something close to it. Create a script that inserts a placeholder window with the private flag set. Then query its dimensions somehow and create a monitor with those dimensions. So you basically place the monitor on top of a transparent fixed-size window. You will also need to subscribe to events in order to move the newly created monitor out of scope when you change to another desktop of the parent monitor, otherwise it will act as sticky node. And you likely will need to change some of you keybindings to handle these additional monitors properly, but it should work.

Probably a better way to do it is to write a script that does a bit of bookkeeping to know which nodes form a monocle node together. So you would have a script for moving window into a monocle node, first it would swap the selected node with the node currently in the monocle node and the minimize. This way it is as if you moved the node into the monocle node. And by keeping track of the nodes minimized this way, you should be able to write a script that cycles through them.

It can work if we consider that the *_monocle settings can only apply to the root node of a desktop.

@baskerville You mean you are open to implement my second suggestion in bspwm itself? It could be as simple as having a monocle field in the node state, that is a point to another node (linked list style). I think this will be simpler than having it e.g. in the desktop state, because having it in the node, makes moving it across desktops simpler. And having a bspc node --to-monocle command which puts a node at the head of that linked list. Or you could just use bspc node --to-node and have it behave differently if a monocle flag has been set to it. And some commands will need updating to add support for this, handling the focus of a monocle node that is not being shown at that point, the same for swap and to node. Change in size should apply to all monocle nodes, the same for layer. Setting a monocle node to floating should just move that specific node of the monocle nodes to be floating, so it will need to be cut from the linked list, the same for closing and killing.

I think the above should work, so it would involve a lot of special cases, but they should be simple. However I did notice the issue of how to deal with the difference between the monocle and the node, so if you say to desktop, do you want the whole monocle node to be transplanted to the desktop or do you just want the currently focused node from the monocle node to be transplanted? This probably could be solved by adding a node modifier called monocle, so you can specify whether you want it to apply to the node or the monocle as a whole. So if you have windows A, B, and C, and they form a monocle together, if you would say bspc node B.monocle --to-desktop ... it would move the whole monocle of which window B is part of. On the other hand, for the head of the linked monocle list of nodes it makes more sense to approach it from the inverse, there it would be more intuitive to expect the whole monocle to move by default. Maybe the leaf modifier could be reused for this use case as well instead introducing a new monocle modifier. So bspc node B --to-desktop ... would move the monocle B is part of, but bspc node B.leaf --to-desktop ... would move only B itself.

@msteen No, I meant that apply_layout would require minor adjustments to read the layout attribute from the nodes instead of reading it from the desktop. The layout would then be a node property that propagates to the children. And CYCLE_DIR.same_layout could be used to browse through the monocle leaves.

@baskerville And those changes are enough to allow you to have monocle nodes within otherwise tiled layout, i.e. to have a sub tree be in monocle layout while the rest is tiled / pseudo_tiled? If the way you apply the layout allows for this, this indeed seems the better approach.

Would this mean you could also mix it the other way, to have a tiled sub tree within your otherwise monocle layout? And things like moving a window in a direction, would this still work properly across these different layouts? I doubt this is the case, but if it were to work like that, it would be really powerful.

And those changes are enough to allow you to have monocle nodes within otherwise tiled layout, i.e. to have a sub tree be in monocle layout while the rest is tiled / pseudo_tiled?

Yes.

Would this mean you could also mix it the other way, to have a tiled sub tree within your otherwise monocle layout?

I didn't consider this but it would still work.

And things like moving a window in a direction, would this still work properly across these different layouts?

I think so.

It is the one feature (layouts on a level lower than the desktop) that made me initially prefer herbstluftwm over bspwm, so it would be awesome if this could be implemented!

If this feature could be realized, it makes me wonder about the way bspwm currently is structured, why even have the concept of desktops? Why not merge them with the concept of nodes and allow nodes to be labeled in general? The root node of a monitor would then always be in monocle layout and a desktop amounts to no more then a node with a label within this root node.

And if you would allow labeled nodes to be placed in multiple places at the same time, this would open a whole lot of new possibilities. The one obvious problem of allowing nodes to be placed in multiple places would be, what if a window needs to be shown at two places at the same time, but this would be solvable by e.g. having a policy of last focused location is being shown, the other being replaced by some kind of placeholder.

I know this probably would require quite an overhaul, so even if you were to agree with me, I do not expect this to happen, just some thoughts.

This is one of the things I really miss from i3. Being able to do things like float a tiled container would be amazingly powerful (i3 can do this, but resizing is a little weird).

And if you would allow labeled nodes to be placed in multiple places at the same time, this would open a whole lot of new possibilities.

I'm curious what this might be used for?

@gandalf3 One use case I have in mind is that it allows you to have variations of the same desktop. This is especially useful if you have a single monitor. So what you would have is a certain set of windows that you would want to show on e.g. two desktops, but besides those windows you would also like to have sometimes window A and sometimes window B. This could be a browser, a great example of this Chrome with one of the tree style tabs extensions which introduces a window beside the browser window that contains all your tabs in a tree. So the set of windows you would want to show on multiple desktops would then be Chrome and its extension window, and at the side you would want to switch between a code editor of some kind and some specialized text editor for writing documentation. And maybe the text editor also has a file manager open at the same time, so this could be more complex than just the single window. And thus being able to share a sub tree of windows between desktops, would make tiling window managers a lot more usable, especially so on single monitor setups.

It is a big reason why I did not want to use a tiling window manager when I only had a single monitor.

If this feature could be realized, it makes me wonder about the way bspwm currently is structured, why even have the concept of desktops? Why not merge them with the concept of nodes and allow nodes to be labeled in general? The root node of a monitor would then always be in monocle layout and a desktop amounts to no more then a node with a label within this root node.

It was considered and it wouldn't work very well because a binary tree has at most two children.

One of the feature I wanted to add is the ability to zoom on a particular subtree, i.e. to change the view point (cf. TODO). This can already be achieved, somehow, with:

for id in $(bspc query -N -n .leaf.local); do
    value=$(bspc query -N SUBTREE -n ${id}.descendant_of > /dev/null && echo off || echo on)
    bspc node $id -g hidden=${value}
done

If we want labels on nodes, we might need to get rid of the current automatic insertion mode (and replace it with a simple split along the longest side policy), because it won't allow things like bspc rule -a ... node=~foo (where ~ is a potential label prefix notation). This works fine with the manual insertion mode: we just need to move the label to the newly created internal node, and we end up with what we expect: the new leaf and the insertion point are children of a node labelled foo. But the automatic insertion mode relocates the tree rooted at the parent of the insertion point and we don't want a bunch of unrelated leaves to suddenly have foo as ancestor.

It was considered and it wouldn't work very well because a binary tree has at most two children.

It was more of a conceptual thought than an actual suggestion for a concrete implementation. I would agree that a binary tree would not be the optimal data structure for them. However I still think it would be a big gain in flexibility, if a desktop would be more like a node, and a node (optionally) more like a desktop.

One of the feature I wanted to add is the ability to zoom on a particular subtree, i.e. to change the view point

If you were to implement what I suggested, the zoom feature would require no special support.

And if you would allow labeled nodes to be placed in multiple places at the same time, this would open a whole lot of new possibilities.

In order to zoom a subtree, you would just label the subtree node, and copy it (a reference to it at least) as a desktop. So you can still go back to the unzoomed desktop with everything present as it was, and if you want the zoomed view, you would just go the the newly created desktop (labeled node).

If we want labels on nodes, we might need to get rid of the current automatic insertion mode

I was thinking more on the lines of regarding labeled nodes as (inner) desktops, so they could be considered to be one big node. Also if you would add a new node within a labeled node, it would only consider the space within its labeled parent node. Those two rules would keep things mostly as they are with regards to automatic insertion (as far as I know) and would match with the concept of a desktop as a node (which is what I had in mind).

My ideal window manager is close to bspwm in its current form, at least it is the closest to it of the window managers that I know of. What follows is just my wishlist and most would require a rewrite, so consider them just as ideas.

  • Reimplement it in Rust.
  • Separate the layouting state and make the layout pluggable with at least the tiling and monocle layout provided by default.
  • Separation of X and those parts of the window manager that are agnostic to the backend, such that Wayland support is possible without having to create a complete separate version for it with a lot of overlap.
  • Being able to define a script that at startup defines which applications should be started and where they should go in the layout with more control than the current rules would allow by leveraging being able to get the PID of a window consistently.
  • Allow desktops as nodes (sort of).

With desktop as nodes I am roughly thinking about this, which skips over most details that are irrelevant to get my point:

monitor_t
  uint32_t id
  char* name
  xcb_window_t root
  frame_t *desktop
  monitor_t *prev
  monitor_t *next

frame_t
  uint32_t id
  char* name
  layout_t *layout;
  node_t *root
  node_t *focus

node_t
  uint32_t id
  client_t *client;
  frame_t *frame;

Or if you want to keep the list of desktops per monitor approach instead of externalizing this, you could simple add:

desktop_t
  frame_t *frame
  desktop_t *prev
  desktop_t *next

And change monitor_t from frame_t *desktop to desktop_t *desktop. So a desktop is just a reference to a frame with fixed dimensions. I first thought it would still require some separate desktop type with an id and name, because you generally would want an indirection added to a subtree (frame), but this can be done with a frame as well, just have a frame point to a single frame. It having fixed dimensions (by default) could then just be a property the frame's layout.

If you could constrain a node to only hold a frame, then you could even make the list of desktops per monitor as part of the layout. The tiled layout could store the nodes in a binary tree and the monitor's frame could store the nodes ("desktop" frames) in a doubly-linked list.

Being able to have a specific node in a stacked/monocle/tabbed mode is the one feature which currently holds me back from using bspwm. Otherwise, I think conceptually bspwm is the best wm out there. I often have an editor on one side which always needs to be visible and other programs stacked on each other on the other side (firefox, pdf-reader, other programs which briefly pop up) and I need to rotate between the stacked programs. Like the tabbed layout in i3 or the deck-patch in dwm (https://dwm.suckless.org/patches/deck/).

Being able to have a specific node in a stacked/monocle/tabbed mode is the one feature which currently holds me back from using bspwm. Otherwise, I think conceptually bspwm is the best wm out there. I often have an editor on one side which always needs to be visible and other programs stacked on each other on the other side (firefox, pdf-reader, other programs which briefly pop up) and I need to rotate between the stacked programs. Like the tabbed layout in i3 or the deck-patch in dwm (https://dwm.suckless.org/patches/deck/).

I agree, bspwm is one of the most customizable wm's out there. But not being able to stack/tab windows is unfortunate. I personally don't get why more wm's don't have this feature natively. It only makes sense to be able to stack windows. I've seen people use suckless's tabbed program to achieve this, but this is a problem since bspwm will you see the entire tabbed as one program. So this becomes a problem is you wanna say move a window from one stack(tab) to another.

Is there any news on this feature being implemented? Or if anyone figured out a script to achieve it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

btlvr picture btlvr  路  5Comments

lukasino1214 picture lukasino1214  路  4Comments

icf20 picture icf20  路  5Comments

alecive picture alecive  路  7Comments

rien333 picture rien333  路  5Comments