From 3d943ddd31d3b88b63ec50e1e3470687b0cd09b0 Mon Sep 17 00:00:00 2001 From: Michael Mogenson Date: Wed, 30 Apr 2025 09:57:15 -0400 Subject: [PATCH] MouseFollowsFocus: avoid getting the mouse stuck in screen corners The mouse cursor moves in a straight line between its current position and the destination set with hs.mouse.absolutePosition(). If the mouse is moving to a new screen, it can get caught on a corner that does not connect between screens, depending on the screen sizes and layout. When moving the mouse between screens, first move the mouse to the center of the current screen, then to the center of the new screen, then the target destination. This path has the best chance at avoiding any non-connected edges between screens. These extra mouse movements happen instantaneously and are not visible to the user. --- Source/MouseFollowsFocus.spoon/init.lua | 9 ++++++++- Spoons/MouseFollowsFocus.spoon.zip | Bin 2475 -> 2575 bytes 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/MouseFollowsFocus.spoon/init.lua b/Source/MouseFollowsFocus.spoon/init.lua index a6646483..7fdb813a 100644 --- a/Source/MouseFollowsFocus.spoon/init.lua +++ b/Source/MouseFollowsFocus.spoon/init.lua @@ -55,7 +55,7 @@ function obj:start() }) self.window_filter:subscribe({ hs.window.filter.windowFocused - }, function(window) + }, function(window) if self.onChangeOfScreenOnly and self.currentWindowScreen and self.currentWindowScreen:id() == window:screen():id() then return end self:updateMouse(window) self.currentWindowScreen = window:screen() @@ -91,6 +91,13 @@ function obj:updateMouse(window) local current_pos = hs.geometry(hs.mouse.absolutePosition()) local frame = window:frame() if not current_pos:inside(frame) then + local current_screen = hs.mouse.getCurrentScreen() + local window_screen = window:screen() + if current_screen and window_screen and current_screen ~= window_screen then + -- avoid getting the mouse stuck on a screen corner by moving through the center of each screen + hs.mouse.absolutePosition(current_screen:frame().center) + hs.mouse.absolutePosition(window_screen:frame().center) + end hs.mouse.absolutePosition(frame.center) end end diff --git a/Spoons/MouseFollowsFocus.spoon.zip b/Spoons/MouseFollowsFocus.spoon.zip index 8903c1159ebd9f589fa13931f2eae6543d3e536d..4e7eeea6e81f587b09dca355bfc59c8991df03d1 100644 GIT binary patch delta 1406 zcmZ22+%Lix;LXg!#Q*{yeda|?D1 z7{vYOMRlcqR7+%GU|7aGF-u!gPDdys0*KMH>M!EXT~jJknB(Oo7Ur{biR z)c@{tKea9A#GNsR=H}_fXKH@Ex31(~l>gi_&x`*znwCGv z;_IE>v{vj?A+w*b;q#J9s&l>8pW(j29s0udP4>DUd^cU%_oS_QWM&&t7b-_TPca!wbUze3ntz~8;N**h8C~y_S7;un)z$eSZ?l@| z?!pUkv;ALOySgCop2O>wBR6)a&i)cq;zY*(N{f|A7}dMic9;(uKY} zd;IBTWmEm7cJ?#f|2e(==Om;R9GBIek$fa<&BGJbg^6ETH*UNyR{csfZrOcSsqEOb zz3P7t>3j-{>6&x+yZm3x+)IFGg^poJ(DLzqhtp66wyRT@}EvKH} z>)60rcEN3*P?6Jx9XX9xLXYwq)~CB|=;itGcj}9MUu?<+3ZG>yo!4 z;#my+4=k7iK1|wriSZVD=Vf)lb!t2Yr;TPb*7{Uoo&+vKmiib@{aE7xxAlbl{3v!(FuNwHhHdL8Rn@A7Ti za_=x(zxCd7(U{lY@}oZ1#qVNBsyq0=S#!y20cr6f1;K*(2G+?g2dDdo%$&R7P;wgQ zxh;R#GS8pi%%k|(s^@H1$mGC>iSK9Tnwyy?9bWv*Rp;RrF7bBr66V-E&j)8cCf>5? zlU&wyKsL(!`iz+xljxNQ|Q!CcbxlkbXB11;pk7?V# z*q0p3!nxX0gKzOF)@xW!>8tp2iP`Uk{{^{~+^g!ErC2_0dGv1i2L;DWldnYqPrVdZ z>V>!a<+rE#<^NwkQTcmwZ}!!NrR$f-%=fNmk2_hgLNER0y^Gc#W;}gRe|xjg?&MQz zCso{REC{aA^g0~*vE#gLcXdNb^A&sTqXqvDJ=XL)$UMznXHnPV%SUG(f6^ebZDZ87 zzWv>@UTN-MFSV$L%1B$OecbhNft}&}Rvn>9GiA-|?D=lgg?woNv!A z{tp*u>CIdHi-&X0LucC+F8YsUU$ozvYRh{pt$y{r)7b&LXa6shUQ)WKct_J?wvYTD z_9GQTpz><+b`~=+>;=|lX4Yb67OQam2Xb_4#cGu+zgB?FPRw_Sn7e&AcZ0fObi?h zAwY#wD(hwb0;RZkCT3|%z6UA?X+zVpH*DfM$$Eu@-vUG!b?2&-iD*s;*`jme-#6L* zAerNnE+*b+urPc7`*FGZy6f3D^0pVGTD4m*p7QeUxhHeEuNi>P8``yi2U|Q0~7ao6@Vt6L@`n9dxDDULUn3wfZ;m^fL@h5#PRmt`0 z+xM|Ke-g9O7d_h6m-}t&nd}#>x$2i}o_xOcB}y_d<-^8r`$})71y4MgH~-nPn}0NB zv8~PSy*T^zhx@r}UYPe5h+Olq%yd1)^>Y5Ezf=COyM=eR8tn|IPxv-}JAXg-FR%Q3 zP5nR2+ZuN)IP-vOuSSdbnQMz2AMLf;_^a#Ym3((wUZM4u^Obx{_L-SYs1xd|unsYD z|M9KZVySP`r=P!92?n)p+1H)5HFJZgX!Y+!cNRx&SQ^{&@hp{j}EY6GQwYJ$dohY8y(C{FUZNY({ zl2gn#*K7JFrQXiA!TYxc17^*fh z+FO>U)!2LI9eQaPc3zu5?@;=(_2;->Po8WRSI@qwdEHW0!)*z74)$HIzVqO)pZzYb zbEUt<7k}KLpUmJ~ckw~4=2Uq>Yn>+^Ne}D-%C4Pa+Rh&{)B0t~fiq%z-hODx=P)V1;e4c+lT26N$%}t^eA8C@E$deXR<5>Z7<*FMavB zEy2#|xBh27yYV;WvDM!zyUe}L@~7=!IiF$?I$iu_+(+jR6Q0&TdAxa2dHWRWwJ)|Y zznuOhBt*=$(Eq)ydv`miFeaO3e8&gXg<1!&l?ak!O?=XI@Qwdj5CfNt0_?Uu(|tEAaO3+0Te5AwWfm zaMWJ^G++raS(#N&3{*H^77&}$S&NxjK#C{d