sbmpost / AutoRaise

AutoRaise (and focus) a window when hovering over it with the mouse
GNU General Public License v3.0
1.42k stars 60 forks source link

Autoraises after mouse movement within window, not on initial entry #181

Closed web3d0 closed 5 months ago

web3d0 commented 6 months ago

When flicking the mouse rapidly to a corner, the window doesn't auto-raise. Moving the mouse by one pixel subsequently raises (or focuses) the window.

This appears to be because AutoRaise is sampling the last mouse position, rather than the current mouse position. Eg, it is something like "if (movement) raise(last_position)", rather than "if (movement) raise(current_position)".

This problem can be replicated by flicking a finger across the mouse pad into another window, and seeing it doesn't activate.

sbmpost commented 6 months ago

@web3d0

Hi there,

Can you try by flicking the mouse to a bottom corner instead of a top corner? I suspect it may have to do with the top menu bar. If not, then maybe you can record a short video demonstrating the problem?

sbmpost commented 6 months ago

@web3d0

Thinking a bit more about your description, perhaps the problem can be reduced by lowering the mouse polling interval (at the cost of higher cpu usage). You can use the bottom section in the UI for this, or if you are using the command line version, you can specify -pollMillis 20

web3d0 commented 6 months ago

I'm a bit stuck on how to screen record the mouse movements, but will poke into it a bit more. I am flicking to the sides and bottoms of the screen with 50ms delay so I can get to the menu bar without changing apps along the way.

Some background information is that I use "Rectangle" to lay out my windows, so hot corners are turned off and I can flick to change windows. If I flick then move by one pixel, it works fine. If I flick, then sometimes it doesn't work.

I have tested flicking to the middle of a window, to the bottom or left/right edges, or to the corner, and it's the same problem where it needs at least one pixel of movement after rapidly moving between windows.

I think it is possibly the same issue as if you close a window, the the one underneath isn't AutoRaised unless you move a pixel.

I don't think it's a polling issue, even though that would minimise the number of times the problem happens. It really does seem to be a "the last mouse position" rather than "current mouse position".

sbmpost commented 6 months ago

I think it is possibly the same issue as if you close a window, the one underneath isn't AutoRaised unless you move a pixel.

It might indeed be the case. If this is true, there is not much that can be done about it I am afraid. AutoRaise relies on mouse polling to detect mouse movement and the mouse needs to move at least one pixel. For curiosity I checked what happens if I let AutoRaise believe the mouse moved even when it didn't. As expected, CPU usage increases considerably. Theoretically it would solve your problem, but it introduces other unwanted side effects as well.

web3d0 commented 6 months ago

Issue has been answered, no fix within scope of how this works.

web3d0 commented 6 months ago

After pondering about this a bit more, I don't think this is the same problem as if a window is closed without a mouse movement. To replicate my problem, try this on a laptop so you can get the super-fast flicking:

Put a window at top left. Put a window at bottom right. Move the mouse cursor to the top left window, so that it activates. Now flick very rapidly to the bottom right corner.

The bottom right window will activate about half the time, but not always. As there is moue movement, the bug seems to be that it picks up the last mouse co-ordinate for activation, rather than the single sample of the new mouse co-ordinate.

Eg, it seems to be doing "if (mouse_moved) activate_by_stored_coord()", rather than "if (mouse_moved) {update_coord(current); activate_by_stored_coord()}".

sbmpost commented 6 months ago

@web3d0

Thanks for the detailed write up. What AutoRaise does is a small variant of your code. What it does (or should do) is:

loop every 50 milliseconds:
    current = getCurrent;
    if (current != previous);
        activate(current);
    previous = current

The loop is unavoidable because an event based approach only works if you have direct access to the application itself. AutoRaise sits "above" multiple applications and therefore can only poll regularly (the loop) to obtain the global mouse position. That being said it still seems a bit strange it wouldn't detect the mouse movement. I think what you are trying to say is that it might be doing

activate(previous);

instead of

activate(current);

I am going to test with the 2 windows you suggested on my machine. I am not sure if I am able to "flick" the mouse. I think what you mean by that is "very fast movement". I will give it a shot however.

web3d0 commented 6 months ago

The "flick" is on a laptop mousepad rather than a physical mouse. I suspect the sample rate on a physical mouse wouldn't trigger the problem as it's extremely fast, but a mousepad is trying to detect finger movements.

You can see the cursor make large jumps without a smooth extrapolation in between. However it does seem odd that AutoRaise wouldn't pick up the last cursor position as a change in position.

If you can't replicate the problem, happy to try and create a video.

web3d0 commented 6 months ago

https://github.com/sbmpost/AutoRaise/assets/140585/fc522e7a-4180-415b-98e2-dfedd2ac9ecf

This shows normal window switching, then two failed attempts to move to the bottom right window, then one successful attempt as it sometimes works.

My thought is perhaps in your 50ms loop, there might sometimes be more than one event so you're processing the ones in a queue. The fix would be to check if there are more events?

sbmpost commented 6 months ago

@web3d0

Video helps a lot :-) I will have a look when I find the time. Hopefully somewhere this week.

web3d0 commented 6 months ago

It might not be the flick. I can replicate the problem by moving the mouse very slowly so it doesn't autoraise, and into the corner and it doesn't autoraise.

No matter how quickly I flick, if the mouse isn't at the edge of the screen or in the corner, it will always autoraise. So it's literally a corner case!

It's not related to the corner of the window, it's specifically related to edge or corner of screen.

web3d0 commented 6 months ago

https://github.com/sbmpost/AutoRaise/assets/140585/279d9e32-adc4-4bb8-9dc5-4e5871eee205

Fast or slow doesn't matter. Dragging to an edge instead of the corner also has the bug, but half as much.

sbmpost commented 6 months ago

@web3d0

Thanks again for diving deep into this. It helps me to understand what is going on. It may have something to do with the virtual border of a window that was added in monterey (a few mac os versions ago). I wrote a comment about it in the top section of the AutoRaise.mm file. I haven't actually tested this theory however.

web3d0 commented 6 months ago

The problem also happens if the window is partially off screen (eg, the corner of the screen is in the middle of the window). Hopefully you are able to replicate the issue on your machine!

sbmpost commented 6 months ago

@web3d0 The problem should be reduced (mitigated) in branch AutoRaise-4.8. Especially if you lower the polling interval from 50ms to 20ms. If you can let me know if this is indeed an improvement, that would be great.

web3d0 commented 5 months ago

Just seems a bit faster but hasn't really changed anything. Repeating the mouse movement results in the same percentage of failures as having it at 20ms in version 4.7.

I did notice another bug, but not sure whether it is related. If there is a one pixel gap, it won't raise the window underneath, even though it will if you click on it.

Want a video for that?

sbmpost commented 5 months ago

@web3d0

What settings are you using? Assuming you are using the UI, maybe you can make a screenshot of the preferences screen. As for the other bug, let's tackle that after we have the current one solved. If it is related, we will know soon enough.

One other thing: do you have so called "hot corners" enabled? And which terminal program are you using? I noted in the video it looks different from the standard one. This might help me to reproduce more accurately.

web3d0 commented 5 months ago

Screen Shot 2024-03-14 at 4 48 31 pm

I have disabled hot corners, but I notice the bug exists for the sides as well, just half as much. I'm using the default Terminal, I just changed the colours and fonts a bit as I'm an old geezer who used VT102's. :)

I think the second bug may be related, as it has the same "off by one" problem where a single pixel offset will fail to activate the window underneath, but will activate if I click - which is the same behaviour.

My suspicion at the moment is that you are receiving a stream of events from the API, but they're lagged just a bit. So if you're using the last event received as the X,Y co-ord for raising windows, it's off by one.

Another approach could be to receive the event, but then query for the actual X,Y co-ord of the pointer to perform the raise? It would be interesting to know if the event vs current pointer co-ord ever differs.

web3d0 commented 5 months ago

Also forgot to mention that 4.8 has a new bug which 4.7 didn't... after a few hours or a laptop sleep, AutoRaise has to be toggled off and on again to make it work. Somehow it seems to get deactivated over time even though the icon shows that it is still on.

sbmpost commented 5 months ago

@web3d0

after a few hours or a laptop sleep, AutoRaise has to be toggled off and on again to make it work

Ah, It seems that is because AutoRaise crashes with the recent changes I made. I found out why and will fix it.

If you think the one pixel off bug is related to the corner/edge flick bug, then perhaps the video you suggested is helpful after all.

Thanks for providing me with so much detail. It helps :-)

web3d0 commented 5 months ago

First part of the video shows the corner bug still exists, but not as much as before (50ms to 20ms?). Second half of the video shows a window being put over the first window, but with a 2 pixel offset at the bottom. Now mouse to bottom doesn't activate the underlying window until clicked. You can see me moving to the bottom, nothing happens, then I click. I do this twice, so my suspicion is that there is some "around the cursor" or "cursor event lag" where the event itself is reporting an old position and you need to AutoRaise on the current cursor position instead.

https://github.com/sbmpost/AutoRaise/assets/140585/bc007d23-d866-4840-8b17-dcfe356197a4

sbmpost commented 5 months ago

@web3d0

So I spent some more time on these issues. The problem in your last video has to do with MacOS making windows 3 pixels larger than they actually are. This has been documented in AutoRaise.mm like so:

It seems OSX Monterey introduced a transparent 3 pixel border around each window. This
means that when two windows are visually precisely connected and not overlapping, in
reality they are. Consequently one has to move the mouse 3 pixels further out of the
visual area to make the connected window raise. This new OSX 'feature' also introduces
unwanted raising of windows when visually connected to the top menu bar. To solve this
we correct the mouse position before determining which window is underneath the mouse.

AutoRaise corrects the mouse position by moving 3 pixels ahead in the direction you were moving. This works nicely as long as the corrected position falls into the screen area. However on the screen edge there are no pixels left when the mouse is about to leave the screen. A possible fix for this would be that if the screen ends, AutoRaise treats the mouse position as being exactly on the screen edge. This may also fix the problem in the corners of the screen. I will keep you up to date.

sbmpost commented 5 months ago

@web3d0

It was quite a hassle, but I think the screen edge/corner issues are now solved with the latest code I pushed. I also updated the UI (v4.8.1), so testing is easy. You should be able to revert to the default polling interval setting of 50ms which is better for your CPU load. I hope this solves all problems. Let me know if it does ;-)

web3d0 commented 5 months ago

All looks good now! Makes a huge difference to just flick to the window instead of needing to double check! Good one, thanks! :)

web3d0 commented 5 months ago

100% solved