baskerville / bspwm

A tiling window manager based on binary space partitioning
BSD 2-Clause "Simplified" License
7.73k stars 414 forks source link

Closing window is weird #1439

Closed andreykaere closed 1 year ago

andreykaere commented 1 year ago

While testing #1438, I discovered weird behavior in the master version of bspwm. Everything works fine in the latest release, though.

The problem: when I close any window, bspwm closes also conky (which cannot be closed manually). Moreover, when I investigated this, it turns out, that after "close" event, bspwm recieves "focus" event for no reason.

emanuele6 commented 1 year ago

The problem: when I close any window, bspwm closes also conky (which cannot be closed manually).

I don't know what this means, so I don't know how to reproduce it.

Moreover, when I investigated this, it turns out, that after "close" event, bspwm recieves "focus" event for no reason.

Can you clarify what this mean? I don't understand what you are describing.

andreykaere commented 1 year ago

Conky is popular application for showing time, system load etc

How to reproduce the bug: Install conky and run it. Open window on empty desktop (with conky on it) and close it. Result: conky window will be closed as well. But it's not expected behavior.

Another way how to reproduce it: I don't really know the easiest way, but one of the way is the following: install two versions of bspwm. Install ixwindow and run it on both versions of bspwm. Output will be very different.

Another way (maybe easier way) to reproduce it is to write script that subscribes to bspc events like focus node and remove node. And open and close node on empty desktop and watch output.

Yes, my mistake, I forgot to specify, that it happens only when it's the last window on the given desktop.

emanuele6 commented 1 year ago

How to reproduce the bug: Install conky and run it. Open window on empty desktop (with conky on it) and close it. Result: conky window will be closed as well. But it's not expected behavior.

I tried this and the conky window did not close. Please only provide reproduction steps that you have actually tested. If reproducing this bug requires some custom configuration, mention it.

Another way (maybe easier way) to reproduce it is to write script that subscribes to bspc events like focus node and remove node. And open and close node on empty desktop and watch output.

I see that bspwm sends an unnecessary node_focus (saying for the focused node) event before removing the focused node, only if it is the root of the focused desktop. But since it also sends a node_remove event after, and it only happens when you remove the focused node, I don't think it should be considered incorrect, it is just a redundant event.

andreykaere commented 1 year ago

About conky, I will investigate ot further. Maybe there is something in my config.

About sending signal: it's not redundant, it's wrong. Because I receive that signal after window is removed and that's the issue

emanuele6 commented 1 year ago

About sending signal: it's not redundant, it's wrong. Because I receive that signal after window is removed and that's the issue

But, as I said, I recieve the node_focus event, only if the focused node is being removed, and I receive it before the node_remove event.

andreykaere commented 1 year ago

Yes, I agree that it happens when you close focused window.

But about order of events: I think it's not consistent as you are saying. There might be races and if order is wrong, it's not good. Why removing sending unnecessary signal is not something you would like to do?:)

emanuele6 commented 1 year ago

Why removing sending unnecessary signal is not something you would like to do?:)

There are other redundant events that bspwm sends, but you want to remove this one event in particular because you think it is what is causing problems to you. I can reproduce neither of the bugs you have mentioned: "bspwm closing unmanaged conky windows", and "bspwm sending events out of order". You are only guessing that those problems you are experiencing and I cannot reproduce with your steps are related.

You have not provided any meaningful reproduction steps, and have only described the problem with words; you have not even specified how to close the window to reproduce the bug. (I even had to assume "using bspc node -c", but also other methods like using ^D in a terminal running a shell, or using the quit button of some programs, or using bspc node -k do not let me reproduce the bug.)

I can only say that I reliably cannot reproduce the bugs you are describing.

One could patch the code to remove that redundant node_focus event, but I do not see how it is related to your conky problem.

andreykaere commented 1 year ago

Bug is present for both bspc node -c and bspc node -k (but latter is more consistent). About conky: yes, you were right, that you cannot reproduce it, because it occurs because of special script, that hides conky when another window is focused. Below is the script, that works well on stable bspwm, but not good on master version.

#!/bin/bash

bspc subscribe node_focus | while read -r Event Monitor Desktop Node
do
    echo "node_focus"
done &

bspc subscribe node_remove | while read -r Event Monitor Desktop Node
do
    echo "node_remove"
done 

When you close window it prints "node_focus" and after "node_remove". At least, that's what gets printed on my laptop.

emanuele6 commented 1 year ago

When you close window it prints "node_focus" and after "node_remove". At least, that's what gets printed on my laptop.

Of course that would be a race condition. You are subscribing to events from two processes that are running in parallel, and reading the output of those two processes using two other processes (while read -r) that are also running in parallel. It does not make any sense to expect bspwm to have anything to do with the order in which processes get sheduled to read/write/handle the events it sends, bspwm is not your kernel.

There is nothing bspwm can do to fix code like that, if it is not written correctly, the new version of bspwm just makes it more likely for your script that was already not written correctly to not behave as intended because it now refocuses the focused node when the bspwm's root window receives a FOCUS_IN event as other window managers do, because some programs expect that behaviour.

If you care about the order of the events you read, use only one process to read events and subscribe to all the events you want. bspc subscribe node_focus node_remove | while read -r.

I will also mention, since you are probably not just running echo in those while read loops so maybe you are also handling this incorrectly, that you should not assume that node_focus and node_remove events will always give you window ids as their node id. node_focus events are also sent when inner nodes get focused (they notify about bspwm focus, not necessarily xorg input focus), and node_remove is also sent when a receptacle gets removed with node -k.

andreykaere commented 1 year ago

Okay, thank you for your explanation! I have another question though (because documentation lacks this and many more): how do I handle bspc subscribe node_focus node_remove | while read -r? Like, how can I use specific for event fields, like if I do this: bspc subscribe desktop_add desktop_stransfer | while read -r -- how do I know which one event is happening and how do I use its fields?

"that you should not assume that node_focus and node_remove events will always give you window ids as their node id" -- I didn't really get what exactly you mean here. Could you please elaborate on this one?

emanuele6 commented 1 year ago

how do I know which one event is happening and how do I use its fields?

If you run something like bspc subscribe node_focus node_remove desktop_focus, you will see that the first field of each line will be either node_add or node_remove or desktop_focus; this also true for the bpsc subscribe node_remove you were using earlier, the first field of each event, was "node_remove". So you check the first field of the event.

"that you should not assume that node_focus and node_remove events will always give you window ids as their node id" -- I didn't really get what exactly you mean here. Could you please elaborate on this one?

node_focus is fired every time a node is focused, not necessarily a window. So if you focus a inner node, for example by running bspc node -p '@/' in a desktop with two windows, you will get a node_focus event with the node id of that inner node as node id. That is not a X window id, but the internal id that bspwm has assigned to that node. And, node_remove is fired every time a leaf node is removed, this also includes receptacle nodes (not in bspwm 0.9.10, but in the git version it does), so if you create a receptacle node (with bspc node -i), hover your mouse pointer on it, and then run bspc node 'pointed.leaf.!window' -k to close it, you will get a node_remove event with the internal id that bspwm assigned to that receptacle as node id.

You cannot use those node ids for external tools that expect X window ids like xdo/xdotool/xprop/maim/xtitle/etc; bspwm only uses X window ids as node ids for window nodes. You could be using the node ids you get from the events to invoke an external tool that something you do not want to happen when you give it an invalid X window id.

Not knowing what your script actually does since you only showed echo "node_remove" I suggested this as a possible other issue with your script.

andreykaere commented 1 year ago

Okay, thank you a lot. But what do you mean by "inner node"? Like, if I spawned a window and then split it?

In general, what can be a node? Window, receptacle, what else? What is leaf? I would love, if this would be documented somewhere :( I appreciate your help!

In fact, I was planning to write a library in rust, that implements bspc (protocol?). Could you give me a hint, where can I start? Because file bspc.c doesn't contain much ...

emanuele6 commented 1 year ago

Because file bspc.c doesn't contain much ...

bspc just checks its arguments to bspwm as a message, the messages are parsed and handled by bspwm itself, perhaps you want to look at the "src/messages.c" file instead.

Okay, thank you a lot. But what do you mean by "inner node"? Like, if I spawned a window and then split it?

If you have a node that holds the window 1, and then a new window 2 gets inserted into 1, you get a new inner node a.

Before |  After
 1     |    a
      -|>  / \
       |  1   2

In general, what can be a node? Window, receptacle, what else? What is leaf? I would love, if this would be documented somewhere :( I appreciate your help!

Have you read any documentation? Like have you even read the README.md on this github repository (it says "internal" instead of "inner", but that is what I am referring to)?

bspwm, for each desktop, stores windows in the leaves of binary tree data structure, and uses binary space partitioning to decide how the leaves (the leaves of a tree data structure, are the nodes at the bottom, that do not have children; the internal/inner nodes are the other nodes, the ones which have children.) stored in that binary tree will get layed out.

So leaf nodes of the tree either contain a window, or nothing in which case they are called "receptacles" (special leaf nodes that can be created with node -i and are used to pre-occupy tiling space temporarily to be replaced by a window later).

And a "node" is any node in one of those tree.


bspwm also lets you focus inner nodes of the tree with the node -f command.

For example, I have this sxhkd hotkey to select the focused node's parent node:

# Focus the parent of the focused node
super + p
    bspc node @parent -f

Which lets me for example adjust my splits with other hotkeys like:

# Balance the focused node
super + b
    bspc node -B
# Set the split ratio of the focused node to 50%
super + alt + b
    bspc node -r 0.5
# super + z -> layout becomes tiled; all splits are balanced and vertical
# super + x -> layout becomes tiled; all splits are balanced and horizontal
super + {z,x}
    bspc desktop 'focused.!user_tiled' -l tiled; \
    root=$(bspc query -N -n 'focused.!leaf') \
        || exit 1; \
    while bspc node "$root"'#any.!{vertical,horizontal}.!leaf.descendant_of' -y next; do :; done; \
    bspc node "$root" -B

In reality, the hotkeys I use are a bit more complex because I like to default to the root node of the focused desktop if the focused node is not a inner node:

# Balance the focused inner node, or the root node
super + b
        bspc node 'focused.!leaf' -B || \
            bspc node '@/.!leaf' -B
# Set the split ratio of the focused inner node, or the root node to 50%
super + alt + b
    bspc node 'focused.!leaf' -r 0.5 || \
        bspc node '@/.!leaf' -r 0.5
# super + z -> layout becomes tiled; all splits are balanced and vertical
# super + x -> layout becomes tiled; all splits are balanced and horizontal
super + {z,x}
    bspc desktop 'focused.!user_tiled' -l tiled; \
    root=$(bspc query -N -n 'focused.!leaf') \
            || root=$(bspc query -N -n '@/.!leaf') \
        || exit 1; \
    while bspc node "$root"'#any.!{vertical,horizontal}.!leaf.descendant_of' -y next; do :; done; \
    bspc node "$root" -B

Or hotkeys that act on all the nodes of some kind under the focused node, e.g. (_EMANUELE6_INVERT is an atom interpreted by my compositor that inverts the colours of the windows that have it set, this hotkey sets/unsets the atom for all the non-hidden windows under the focused node (.!hidden.window.descendant_of)).

# Set/Unset _EMANUELE6_INVERT
super + ctrl {_,shift + }i
    bspc query -N focused -n '.!hidden.window.descendant_of' | \
    while IFS= read -r wid; do \
        xprop -id "$wid" \
            {-format _EMANUELE6_INVERT 8c -set _EMANUELE6_INVERT 1 \
            ,-remove _EMANUELE6_INVERT}; \
    done

If you want a tool to help you visualise the bspwm tree, you can use bspdeskjson2dot; example:

desktop tree

andreykaere commented 1 year ago

Well, I read README some time ago. I believe man bspwm should be the source of documentation or some other file, but definitely not a README, because for me it's something short (and I believe that's the case here too).

And why does bspwm distinguish "inner" window and "normal" window when it comes to assigning id?

emanuele6 commented 1 year ago

And why does bspwm distinguish "inner" window and "normal" window when it comes to assigning id?

Sorry, but your question does not make sense to me. And "inner windows" are not a thing and no one has ever mentioned them.

I believe man bspwm should be the source of documentation or some other file, but definitely not a README, because for me it's something short (and I believe that's the case here too).

Well, whatever. But I just disagree that the manual page is not describing something or failing as a source of documentation. There is a lot of information in its first pages.


In any case, this is just off topic, and I think this issue should be closed as the bug is not in bspwm, but in your scripts (you could have experienced the same problem even in earlier versions with events like desktop_focus or node_focus that are sent in rapid succession). If you want someone to help you understand how bspwm works, you can join the IRC channel (that is bridged with a matrix room), or alternatively there is the subreddit.

andreykaere commented 1 year ago

"And "inner windows" are not a thing and no one has ever mentioned them" -- sorry, I mean inner nodes. I mean, it's just weird for me, that if I spawn a window on empty desktop, then it has an id from Xorg. But if I spawn another one there, it will have something weird id ... Or I got you wrong?

Yes, I am gonna close this issue. Yes, I agree, matrix room is a better place for this discussion.