awesomeWM / awesome

awesome window manager
https://awesomewm.org/
GNU General Public License v2.0
6.39k stars 597 forks source link

Better mouse focus not based on mouse::enter #3349

Open rwtallant13 opened 3 years ago

rwtallant13 commented 3 years ago

Output of awesome --version:

awesome v4.3-1021-g9c5149d4e (Too long) • Compiled against Lua 5.3.6 (running with Lua 5.3) • API level: 4 • D-Bus support: yes • xcb-errors support: no • execinfo support: yes • xcb-randr version: 1.6 • LGI version: 0.9.2 • Transparency enabled: yes • Custom search paths: no

How to reproduce the issue:

-hover mouse over window with sloppy focus enabled -switch focus to another window through key bindings so that mouse remains hovered over now unfocused window -wiggle mouse OR -awful.autofocus of new windows causes same issue

Actual result:

focus is not regained on window below mouse, because mouse has to enter the window to activate sloppy focus.

Expected result:

focus is regained on window below mouse whenever mouse is moved.

Aire-One commented 3 years ago

The default awesomerc.lua define the sloppy focus feature with:

https://github.com/awesomeWM/awesome/blob/master/awesomerc.lua#L572-L575

You should change the "mouse::enter" signal by "mouse::move" to get the desired behavior. I'm not sure how it would result performance wise, tho...

EDIT: I though GitHub would print the linked code... Anyway, here is the version you should have in your config for the desired behavior:

-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::move", function(c)
     c:activate { context = "mouse_enter", raise = false }
end)
rwtallant13 commented 3 years ago

That does not work.

rwtallant13 commented 3 years ago

in fact it breaks sloppy focus in a weird way.

Aire-One commented 3 years ago

Oops... Sorry my bad, I assumed you use the git version. I guess you use stable awesome, then.

Here is what you should use with the stable version:

client.connect_signal("mouse::move", function(c)
    c:emit_signal("request::activate", "mouse_enter", {raise = false})
end)
Aire-One commented 3 years ago

Wait, you are running the git-version according to the awesome --version from the first message! I just tried to run my fix from my first message (https://github.com/awesomeWM/awesome/issues/3349#issuecomment-846442651), it worked as expected.

Can you elaborate on what's happening?

rwtallant13 commented 3 years ago

https://user-images.githubusercontent.com/10453230/119238577-bc7d0180-bb32-11eb-9c48-dec0bc8054d8.mp4

look at the white border showing the focused window, moving from window to window does not reliably move focus with the setting you gave me

rwtallant13 commented 3 years ago

Along with breaking the way sloppy focus works this still does not solve the issue for me, i still cant wiggle my mouse to regain focus on a hovered window. Not sure if its a problem with my rc.lua but I'll see if i can get it to work the way you say it does for you.

rwtallant13 commented 3 years ago

Even when using the default rc.lua and replacing the default sloppy focus with what you gave above it gives a broken/unreliable sloppy focus so i dont think the problem is with my rc.lua

https://user-images.githubusercontent.com/10453230/119238907-01a23300-bb35-11eb-8521-516969e06ded.mp4

Aire-One commented 3 years ago

I ran a test with the default awesomerc.lua file from git and the following diff:

diff --git a/awesomerc.lua b/awesomerc.lua
index 411377ded..51a5dbe8b 100644
--- a/awesomerc.lua
+++ b/awesomerc.lua
@@ -570,6 +570,6 @@ end)
 -- }}}

 -- Enable sloppy focus, so that focus follows mouse.
-client.connect_signal("mouse::enter", function(c)
+client.connect_signal("mouse::move", function(c)
     c:activate { context = "mouse_enter", raise = false }
 end)

Here is what I get running it with Xephyr:

https://user-images.githubusercontent.com/6602958/119257267-c8190880-bbc4-11eb-8353-407ed39f02a6.mp4

So, yeah... I know "it works on my computer" is not an acceptable answer, but I fail to understand how changing the signal from "mouse::enter" to "mouse::move" can have the result you recorded...

Aire-One commented 3 years ago

I ran some more tests with different client layout to mimic what you did with your last screen-capture.

At some point, I get this out: 2021-05-23 12:54:08 W: awesome: a_glib_poll:470: Last main loop iteration took 0.241485 seconds! Increasing limit for this warning to that value.

I guess, if you have low-end hardware the "mouse::move" event can be triggered too much time too quickly and creates race condition on the focus...

Maybe you can try to "optimize" the callback with something like this: (untested)

client.connect_signal("mouse::move", function(c)
     if client.focus ~= c then
         c:activate { context = "mouse_enter", raise = false }
    end
end)
unai-ndz commented 3 years ago

Maybe gears.timer.delayed_call can help with performace.

Alternatively what I do is focus the client when it receives a mouse click. Not exactly the same but solves the same problem while being much more performant.

client.connect_signal('request::default_mousebindings', function()
    awful.mouse.append_client_mousebindings {
        awful.button {
            button = awful.button.names.LEFT,
            group = 'Client: Control',
            description = 'Focus client under mouse',
            on_press = function(c)
                c:activate { context = 'mouse_click' }
            end,
        },
        awful.button {
            button = awful.button.names.RIGHT,
            group = 'Client: Control',
            description = 'Focus client under mouse',
            on_press = function(c)
                c:activate { context = 'mouse_click' }
            end,
        },
       ...
Aire-One commented 3 years ago

Maybe gears.timer.delayed_call can help with performace.

I experimented a bit in this direction. IIRC the event trigger wait the end of the GLib loop to call the callback function, so it shouldn't change much... Anyway, I ended-up with the delayed_call + a lock:

-- Enable sloppy focus, so that focus follows mouse.
mutex = false
client.connect_signal("mouse::move", function(c)
    print('client mouse::move envent triggered')

    if client.focus ~= c then
        print('we can change focus')

        if not mutex then
            print('we request focus change')
            mutex = true

            gears.timer.delayed_call(function ()
                print('we are in delayed_call to change focus')
                c:activate { context = "mouse_enter", raise = false }
                mutex = false
            end)
        end
    end
end)

I got this output when moving the mouse quickly between clients:

client mouse::move envent triggered
...
client mouse::move envent triggered
we can change focus
we request focus change
we are in delayed_call to change focus
client mouse::move envent triggered
client mouse::move envent triggered
we can change focus
we request focus change
we are in delayed_call to change focus
client mouse::move envent triggered
...

The 3 prints "we blahblah" are always called one after the other in this exact order... So I suspect my early assumption is correct, the call stack "puts" the delayed_call right after the mouse::move event.

Alternatively what I do is focus the client when it receives a mouse click. Not exactly the same but solves the same problem while being much more performant.

This is an alternative solution that I would recommend! IIRC this is also how Windows and MacOS behave, so it's probably also more intuitive for new users.

unai-ndz commented 3 years ago

the event trigger wait the end of the GLib loop to call the callback function

This makes a lot of sense. And thanks for testing it.