baskerville / bspwm

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

Google Chrome / Electron apps don't "unfocus" when changing desktop #811

Open drasill opened 6 years ago

drasill commented 6 years ago

Hi,

when focusing a window, if the previous window is Chrome, it doesn't "unfocus", if they are not both on the same desktop.

Exemple :

I did confirm it by typing this in the DevTools of any chrome window :

setInterval( function(){ console.log( document.hasFocus()); }, 500 );

✔ The "focus" goes from true to false when I focus another window on the same desktop. ✘ The "focus" keeps being true when I focus another window on another desktop.

This doesn't happen with firefox. I suppose Chrome doesn't have the same method to check it's focused state.

It's a real problem for some Electron applications (like discord), which notifies you when focus is lost. In this case, changing desktop doesn't show notifications.

Thank you for reading :)

ar1a commented 5 years ago

Another bump! Would love to know if any progress has been made, and what we can do about it!

m3rcuriel commented 5 years ago

A third bump, this is causing problems for me with notifications as well -- happy to look into it if I know whether Electron or BSPWM are in the wrong.

drasill commented 4 years ago

Also, switching to a desktop, which has a float window in it, produces the same bug.

But, it does not bug if the mouse cursors is on the window after the switch; the electron window is correctlyunfocused.

A first I though it was because of having focus_follows_pointer=true, but even with this option set to false, the behavior does not change.

I think BSPWM is in the wrong, since the bug differs based on where we are focusing.

cubetastic33 commented 4 years ago

I too face this issue, and it's very annoying. For me, the issue is with discord notifications - I don't get notifications of the focused channel if I change workspaces normally with discord in focus in that workspace. My workarounds are focusing a different window before changing workspaces, or going to the Friends tab so I get notifications.

I found this reddit post from 5 months ago, whose OP had the exact same issue as me. They've found this interesting thing:

I found something strange. If I switch desktop by using the keyboard shortcut, the notification does not work. But if I switch desktop by using the mouse click on polybar, the notification works. Maybe polybar use different commands to switching desktop ?

And they've also come up with a workaround to the normal workspace-switching command.

Why hasn't there been any progress in this issue for nearly 2 years now?

drasill commented 4 years ago

Ok, the workaround @cubetastic33 mentioned is working great, although it is ugly as hell.

I've replaced all bspc node -d or bspc desktop -f by calls to a script :

#!/bin/sh
eval "$(xdotool getmouselocation --shell)"
xdotool mousemove 960 1080
if [ "$2" = "move" ]; then
    bspc node -d "$1"
elif [ "$2" = "follow" ]; then
    bspc node -d "$1" -f
else
    bspc desktop -f "$1"
fi
xdotool mousemove --screen $SCREEN $X $Y
cubetastic33 commented 4 years ago

Indeed. Why can't we fix this in bspwm's source code?

mixedCase commented 4 years ago

@baskerville Any ideas what could be going wrong? Unfortunately all the most popular communication apps are Electron-based and thus all their messaging notifications are affected.

dakyskye commented 4 years ago

I do experience the same, subscribed.

instantepiphany commented 4 years ago

@baskerville I'm facing the same thing, with Slack, Discord, etc. I would be happy to look at this if you could point me in the right direction.

instantepiphany commented 4 years ago

Ok, the workaround @cubetastic33 mentioned is working great, although it is ugly as hell.

I've replaced all bspc node -d or bspc desktop -f by calls to a script :

#!/bin/sh
eval "$(xdotool getmouselocation --shell)"
xdotool mousemove 960 1080
if [ "$2" = "move" ]; then
  bspc node -d "$1"
elif [ "$2" = "follow" ]; then
  bspc node -d "$1" -f
else
  bspc desktop -f "$1"
fi
xdotool mousemove --screen $SCREEN $X $Y

I've done some reading and testing. I can reliably reproduce this issue. If I have slack open on a desktop, and switch to another desktop using the default sxhkd keyboard shortcut, slack does not send a notification to ewmh. Slack even marks the message as read.

However, if I move my mouse so it is not hovering over the slack window, and then use the keyboard shortcut to change desktops, I correctly get the notification.

My suspicion is that at some point when switching desktops, either 1. slack checks the mouse position or 2. bspwm does.

I haven't tested this with Discord (don't have a good way to do that), but I suspect it would show the same behaviour.

This explains why the above workaround fixes the problem - the mouse is moved away from the slack window before switching desktops.

I'm still digging through bspwm's code to see if the issue is bspwm, or electron.

baskerville commented 4 years ago

The X.Org events can be recorded.

cubetastic33 commented 4 years ago

I think the issue is likely with bspwm, because this issue doesn't happen with other WMs like i3, for example. And I've been facing this issue for so long that I always move my mouse outside discord from muscle memory :joy: It would be really cool if this issue were fixed

baskerville commented 4 years ago

@instantepiphany do you have focus_follows_pointer or pointer_follows_focus set in bspwmrc?

instantepiphany commented 4 years ago

@baskerville no, just the example config: image

instantepiphany commented 4 years ago

And the behaviour I described above isn't affected by whether slack is focused or not - I tried opening a terminal window beside it and the only variable that changed whether or not I got the notification was if my mouse was inside the slack window when I switched desktops.

For example, with only the slack window open, if I move my mouse to the gaps so it isn't inside the window, then switch desktops and trigger a notification, I receive it correctly.

instantepiphany commented 4 years ago

I have windows I can't close, so I can't test this right now, but I wonder if we need to inform ewmh of an active window change? image

Edit:

Actually, if the above were the case, I would be able to see that in xprop, which I can't. xprop shows everything set correctly (_ACTIVE_WINDOW is 0x0 once the desktop is switched).

If this really doesn't happen in i3, I wonder if i3 has a workaround inbuilt to force apps to believe they aren't active/focused anymore.

emanuele6 commented 4 years ago

I made this simple electron program to reproduce this bug with:

index.js:

const { exec } = require("child_process");
const { app, BrowserWindow } = require("electron");

app.whenReady().then(function() {
    const win = new BrowserWindow();
    win.loadURL("https://github.com/baskerville/bspwm/issues/811");
});

app.on("browser-window-focus", function(e, win) {
    console.log("hello");
    exec("notify-send hello").unref();
});
app.on("browser-window-blur", function(e, win) {
    console.log("bye");
    exec("notify-send bye").unref();
});

package.json:

{
  "name": "electron-temp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "electron ."
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "child_process": "^1.0.2",
    "electron": "^9.1.2"
  }
}

Put these two files in a directory and then run npm install.

To launch the program, run npm start.

With this program, it's very easy to reproduce (especially for me since I don't use discord or slack).

emanuele6 commented 4 years ago

https://github.com/baskerville/bspwm/issues/811#issuecomment-667889182

@instantepiphany, I tried to use that patch and the problem wasn't fixed.

emanuele6 commented 4 years ago

@baskerville, that commit didn't fix the issue.

emanuele6 commented 4 years ago

@baskerville https://youtu.be/Xy9TLXJmL54

baskerville commented 4 years ago

@emanuele6 I've used your test case: the script previously didn't print bye when switching desktops and now it does.

Am I missing something?

emanuele6 commented 4 years ago

@emanuele6 I've used your test case: the script previously didn't print bye when switching desktops and now it does.

Am I missing something?

The issue is that it only prints bye when you switch desktops while the pointer isn't hovering the window. It doesn't print bye if you switch desktop while the pointer is hovering the window. (← issue)

emanuele6 commented 4 years ago

@emanuele6 I've used your test case: the script previously didn't print bye when switching desktops and now it does.

No, before d4dee39, it also printed bye if you changed desktop while the pointer wasn't hovering the window.

d4dee39 didn't change the behaviour of the electron program. It seems that d4dee39 didn't accomplish anything.

baskerville commented 4 years ago

@emanuele6 You're right: in fact I think the electron window really wants a LeaveNotify event. So if you have a floating window in another desktop, and your pointer is inside this window, you can leave the pointer inside the electron window, and when the desktop is switched, your test script will print bye!

instantepiphany commented 4 years ago

@baskerville So does that mean LeaveNotify are currently not sent to a window if that window has the cursor over it and you switch to an empty desktop? And the fix would then be to send this event in that case.

kristoferus75 commented 4 years ago

I just saw that the case was opened on May 31, 2018 that is now 2 years and no progress in sight ?

kristoferus75 commented 4 years ago

Hi !

I dont know if this help :

in herbstluftwm -> the chrome browser no reaction until my claws-mail is opened and i need manualy close it -> badly that on bspwm :-)

in spectrwm -> no problem it works -> no issue !

my issue: https://github.com/baskerville/bspwm/issues/1174

regards kristoferus75

baskerville commented 4 years ago

I've noticed that both spectrwm and i3 pass the timestamp of the last received event as the last argument to xcb_set_input_focus while bspwm passes XCB_CURRENT_TIME.

emanuele6 commented 4 years ago

I've noticed that both spectrwm and i3 pass the timestamp of the last received event as the last argument to xcb_set_input_focus while bspwm passes XCB_CURRENT_TIME.

I tried to use this patch, but it didn't solve the problem:

diff --git a/src/events.c b/src/events.c
index 3b82c96..8ff6919 100644
--- a/src/events.c
+++ b/src/events.c
@@ -36,6 +36,7 @@
 #include "events.h"

 uint8_t randr_base;
+xcb_timestamp_t last_event_timestamp = 0;

 void handle_event(xcb_generic_event_t *evt)
 {
@@ -86,6 +87,7 @@ void handle_event(xcb_generic_event_t *evt)
            }
            break;
    }
+   last_event_timestamp = XCB_CURRENT_TIME;
 }

 void map_request(xcb_generic_event_t *evt)
diff --git a/src/events.h b/src/events.h
index f144dcc..1c4bdc1 100644
--- a/src/events.h
+++ b/src/events.h
@@ -31,6 +31,7 @@
 #define ERROR_CODE_BAD_WINDOW  3

 extern uint8_t randr_base;
+extern xcb_timestamp_t last_event_timestamp;
 static const xcb_button_index_t BUTTONS[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};

 void handle_event(xcb_generic_event_t *evt);
diff --git a/src/window.c b/src/window.c
index cd2340d..83b523a 100644
--- a/src/window.c
+++ b/src/window.c
@@ -29,6 +29,7 @@
 #include <xcb/shape.h>
 #include "bspwm.h"
 #include "ewmh.h"
+#include "events.h"
 #include "monitor.h"
 #include "desktop.h"
 #include "query.h"
@@ -928,7 +929,7 @@ void set_input_focus(node_t *n)
        clear_input_focus();
    } else {
        if (n->client->icccm_props.input_hint) {
-           xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, XCB_CURRENT_TIME);
+           xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, last_event_timestamp);
        } else if (n->client->icccm_props.take_focus) {
            send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
        }

NOTE: adding last_event_timestamp = XCB_CURRENT_TIME; before the switch block instead of after the switch block doesn't solve this either.

baskerville commented 4 years ago

@emanuele6 last_event_timestamp = XCB_CURRENT_TIME is not going to help, what we want is last_event_timestamp = e->time in each event handler…

emanuele6 commented 4 years ago

Oh, sorry about that, I don't have much experience at all with libxcb.

what we want is last_event_timestamp = e->time in each event handler…

It doesn't look like event types have a time field (or any field of type xcb_timestamp_t) though, example:

typedef struct xcb_map_request_event_t {
    uint8_t      response_type;
    uint8_t      pad0;
    uint16_t     sequence;
    xcb_window_t parent;
    xcb_window_t window;
} xcb_map_request_event_t;

Maybe there is a function to call to get the timestamp out of an event?

baskerville commented 4 years ago

Some of the events do have a time field, for example xcb_enter_notify_event_t.

emanuele6 commented 4 years ago

Applying this patch changes the behaviour a little, but it's a bit random (maybe that's just because this is a random change...): https://youtu.be/RUmV8NxWUT8 .

The window behaves like usual, but, if you focus a different desktop and then go back, the window starts getting "focus" and "blur" events also when the pointer enters/exits the window; then, if you switch to another window with a keybind, the window keeps having that behaviour even though it's not focused, but as soon as you refocus it with the mouse, it behaves like usual again. Also, the cursor on my urxvt window looked as if the window wasn't focused even though it was focused when the test window was in that weird state.

diff --git a/src/events.c b/src/events.c
index 3b82c96..1f3b8a3 100644
--- a/src/events.c
+++ b/src/events.c
@@ -36,6 +36,7 @@
 #include "events.h"

 uint8_t randr_base;
+xcb_timestamp_t last_event_timestamp = 0;

 void handle_event(xcb_generic_event_t *evt)
 {
@@ -364,6 +365,8 @@ void focus_in(xcb_generic_event_t *evt)
 void button_press(xcb_generic_event_t *evt)
 {
    xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt;
+   last_event_timestamp = e->time;
+
    bool replay = false;
    for (unsigned int i = 0; i < LENGTH(BUTTONS); i++) {
        if (e->detail != BUTTONS[i]) {
@@ -389,6 +392,8 @@ void button_press(xcb_generic_event_t *evt)
 void enter_notify(xcb_generic_event_t *evt)
 {
    xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt;
+   last_event_timestamp = e->time;
+
    xcb_window_t win = e->event;

    if (e->mode != XCB_NOTIFY_MODE_NORMAL || e->detail == XCB_NOTIFY_DETAIL_INFERIOR) {
@@ -414,6 +419,7 @@ void enter_notify(xcb_generic_event_t *evt)
 void motion_notify(xcb_generic_event_t *evt)
 {
    xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *) evt;
+   last_event_timestamp = e->time;

    static uint16_t last_motion_x = 0, last_motion_y = 0;
    static xcb_timestamp_t last_motion_time = 0;
diff --git a/src/events.h b/src/events.h
index f144dcc..1c4bdc1 100644
--- a/src/events.h
+++ b/src/events.h
@@ -31,6 +31,7 @@
 #define ERROR_CODE_BAD_WINDOW  3

 extern uint8_t randr_base;
+extern xcb_timestamp_t last_event_timestamp;
 static const xcb_button_index_t BUTTONS[] = {XCB_BUTTON_INDEX_1, XCB_BUTTON_INDEX_2, XCB_BUTTON_INDEX_3};

 void handle_event(xcb_generic_event_t *evt);
diff --git a/src/window.c b/src/window.c
index cd2340d..83b523a 100644
--- a/src/window.c
+++ b/src/window.c
@@ -29,6 +29,7 @@
 #include <xcb/shape.h>
 #include "bspwm.h"
 #include "ewmh.h"
+#include "events.h"
 #include "monitor.h"
 #include "desktop.h"
 #include "query.h"
@@ -928,7 +929,7 @@ void set_input_focus(node_t *n)
        clear_input_focus();
    } else {
        if (n->client->icccm_props.input_hint) {
-           xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, XCB_CURRENT_TIME);
+           xcb_set_input_focus(dpy, XCB_INPUT_FOCUS_PARENT, n->id, last_event_timestamp);
        } else if (n->client->icccm_props.take_focus) {
            send_client_message(n->id, ewmh->WM_PROTOCOLS, WM_TAKE_FOCUS);
        }

For reference, this is the keybind I used in the video (super + g) to focus to the urxvt window:

# Focus a floating window / Cycle floating windows.
super + {_,shift + }g
    bspc node 'focused.floating#{next,prev}.local.!hidden.floating' -f \
        || bspc node 'last.local.!hidden.floating' -f \
        || bspc node  'any.local.!hidden.floating' -f
kristoferus75 commented 4 years ago

on dwm the same bug -> if i open dmenu on same workspace on which is the browser -> submenu not work :-(

kristoferus75 commented 4 years ago

It also happens with visual code :-(

I have now also open a issue on chromium bug site:

https://bugs.chromium.org/p/chromium/issues/detail?id=1117555

baskerville commented 4 years ago

It might be worth noting that i3 and spectrwm are reparenting WMs while bspwm and herbstluftwm aren't.

kristoferus75 commented 4 years ago

the same problem also on opera browser !

soenkeliebau commented 3 years ago

Just stumbled across this issue as well. For me the slack electron app is the most notable effect.

Is there any development being done towards solving this? @baskerville , it seemed like you had a fairly good idea of what needs to happen (send NOTIFY_LEAVE event)? Or was that just the root cause of the issue, not a way towards solving this?

I am pretty much clueless regarding any of this, but happy to help in any way I can towards a solution ...

josefandersson commented 3 years ago

This makes bspwm completely unusable for me and I guess a lot others... dwm doesn't have this issue at all, maybe steal their code? lol Really unfortunate this hasn't been fixed yet

josefandersson commented 3 years ago

@baskerville Changing window.c:905 from set_window_state(win, XCB_ICCCM_WM_STATE_ICONIC); to set_window_state(win, XCB_ICCCM_WM_STATE_WITHDRAWN); sort of fixes the issue for me, but not when the destination desktop is empty (no nodes).

Any reason XCB_ICCCM_WM_STATE_ICONIC was used instead of XCB_ICCCM_WM_STATE_WITHDRAWN? Any clue on why window.c:905 doesn't seem to be called when the destination desktop is empty?

josefandersson commented 3 years ago

Could it be that xcb_change_window_attributes(dpy, root, XCB_CW_EVENT_MASK, values_off); is not called whenever the destination desktop is empty? ie show_node@tree.c:688 just returns because n is null (assuming it's null since d->root of an empty desktop is null right?)

Nvm it is being called through tree.c:629 -> desktop.c:519 -> tree.c:677 -> window.c:912

emanuele6 commented 3 years ago

This makes bspwm completely unusable for me and I guess a lot others... dwm doesn't have this issue at all, maybe steal their code? lol Really unfortunate this hasn't been fixed yet

It's not that easy: dwm is written with libX11 while bspwm is written with libxcb.

drasill commented 3 years ago

(Useless comment) I'm struggling with this bug for 2 years now, I'm very happy that you guys are looking into it. Thank you.

josefandersson commented 3 years ago

It's not that easy: dwm is written with libX11 while bspwm is written with libxcb.

Changing XCB_ICCCM_WM_STATE_ICONIC to XCB_ICCCM_WM_STATE_WITHDRAWN I got from dwm though (not the same macro name bcs different lib but they passed 0 while bspwm passed 2), in the end they still run on xorg

Don't know why bspwm passes 2 normally but changing it to 0 pretty much fixes it for me so that's the build I'm gonna run and I hope my two questions get answered

@drasill It's such a stupid bug for an otherwise great wm, it really is a shame... I'd recommend cloning the git and changing what I said in the previous post, compile and run that build

drasill commented 3 years ago

You are right, this is working great... when the destination is not empty.

It doesn't work neither when going to an empty desktop and then to a non-empty one, focusing any window; and this happen fairly often.

Great work though, it's a nice start.

josefandersson commented 3 years ago

@drasill Yeah it's not perfect but an improvement. I usually populate my desktops in a swoop during startup so it's very seldom I go to an empty desktop, so for me I haven't actually had it be a problem yet. It feels like I've gone followed the stack five times around now and it still looks like it should all work properly, but nope......

edit: The fix doesn't seem to work for destination desktops with some specific applications running in it, for example if the target desktop only contains one IntelliJ window then the previously focused window won't be unfocused like before. This makes me think whether the source app is unfocused is dependent on the target application in some way handling/acknowledging the focus event? I really would need to pick up a book on xorg to solve this, anyone got any recommendations lol

soenkeliebau commented 3 years ago

Hi @baskerville , did you have a chance to check out the fix proposed by @josefandersson yes? If this really works it would be great to get this merged, so we don't need to run of a fork of bspwm.

soenkeliebau commented 3 years ago

@josefandersson just came back to check progress on this issue and actually really read your last comment for the first time. Intellij is a problem child for bspwm anyway - or at least has been for me. Half the time when switching to intellij it doesn't focus properly (for lack of a better word), it does get seem to get the focus, but never shows a cursor and I cannot type. In earlier versions (not sure of what) this was fixable, or at least improveable by setting suppress.focus.stealing=false in the Intellij settings, but as of some time ago this seems to not be effective anymore.

josefandersson commented 3 years ago

@soenkeliebau Really? And it works fine on other WMs? Only problem with IntelliJ for me when running bspwm on Arch is the focus thing in my last comment. The fix I am using rn (and have been since my last comment) is that I have create a small window with xorg library in c and make it floaty and position it outside of the screen and whenever I change desktop (super+num) I move that window first, then switch desktop, then push the floaty window to the bottom the stack aka focus the application on the new desktop. This makes the focus issue completely go away and it is pretty much seamless except for a small flicker in polybar between when the floaty window is sent to the other desktop and the desktop change happens. Wouldn't recommend it, but it works for me. I'll probably leave bspwm behind before this is properly fixed so I'll just roll with this and not spend more time on it.

soenkeliebau commented 3 years ago

@josefandersson I've never really tried it on other wm's to be honest. I have not yet managed to adopt a "mostly keyboard" workflow" I'm sad to say, so it doesn't bother me overly much.

In my opinion that's way of doing things when running linux as desktop os - sure, you can fix everything somehow, but 95% are just not worth the trouble and you just learn to ignore it. You either become a bit stoic about these things or move on to MacOS :)